<commit_before>#include "KeyCode.hpp"

namespace org_pqrs_KeyRemap4MacBook {
#include "keycode/output/include.EventType.cpp"
#include "keycode/output/include.KeyboardType.cpp"
#include "keycode/output/include.ModifierFlag.cpp"
#include "keycode/output/include.KeyCode.cpp"
#include "keycode/output/include.ConsumerKeyCode.cpp"
#include "keycode/output/include.PointingButton.cpp"

  // ------------------------------------------------------------
  bool
  EventType::isKeyDownOrModifierDown(const KeyCode& key, const Flags& flags) const
  {
    if (*this == EventType::DOWN) return true;
    if (*this == EventType::MODIFY) {
      return flags.isOn(key.getModifierFlag());
    }
    return false;
  }

  void
  KeyCode::normalizeKey(Flags& flags, const KeyboardType& keyboardType)
  {
    if (keyboardType == KeyboardType::POWERBOOK ||
        keyboardType == KeyboardType::POWERBOOK_G4 ||
        keyboardType == KeyboardType::POWERBOOK_G4_TI) {
      if (*this == KeyCode::ENTER_POWERBOOK) { *this = KeyCode::ENTER; }
    }

    if (flags.isOn(ModifierFlag::FN)) {
      // Note: KEYPAD_CLEAR has no ModifierFlag::KEYPAD bit.
      if (*this == KeyCode::KEYPAD_0)        { *this = KeyCode::M;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_1)        { *this = KeyCode::J;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_2)        { *this = KeyCode::K;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_3)        { *this = KeyCode::L;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_4)        { *this = KeyCode::U;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_5)        { *this = KeyCode::I;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_6)        { *this = KeyCode::O;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_7)        { *this = KeyCode::KEY_7;        flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_8)        { *this = KeyCode::KEY_8;        flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_9)        { *this = KeyCode::KEY_9;        flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_CLEAR)    { *this = KeyCode::KEY_6;        flags.stripFN(); }
      if (*this == KeyCode::KEYPAD_PLUS)     { *this = KeyCode::SLASH;        flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_MINUS)    { *this = KeyCode::SEMICOLON;    flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_MULTIPLY) { *this = KeyCode::P;            flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_SLASH)    { *this = KeyCode::KEY_0;        flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_EQUAL)    { *this = KeyCode::MINUS;        flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_DOT)      { *this = KeyCode::DOT;          flags.stripFN().stripKEYPAD(); }
      if (*this == KeyCode::PAGEUP)          { *this = KeyCode::CURSOR_UP;    flags.stripFN().add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::PAGEDOWN)        { *this = KeyCode::CURSOR_DOWN;  flags.stripFN().add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::HOME)            { *this = KeyCode::CURSOR_LEFT;  flags.stripFN().add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::END)             { *this = KeyCode::CURSOR_RIGHT; flags.stripFN().add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::ENTER)           { *this = KeyCode::RETURN;       flags.stripFN(); }
      if (*this == KeyCode::FORWARD_DELETE)  { *this = KeyCode::DELETE;       flags.stripFN(); }
    }
  }

  void
  KeyCode::reverseNormalizeKey(Flags& flags, const KeyboardType& keyboardType)
  {
    if (flags.isOn(ModifierFlag::FN)) {
      if (*this == KeyCode::M)            { *this = KeyCode::KEYPAD_0; }
      if (*this == KeyCode::J)            { *this = KeyCode::KEYPAD_1; }
      if (*this == KeyCode::K)            { *this = KeyCode::KEYPAD_2; }
      if (*this == KeyCode::L)            { *this = KeyCode::KEYPAD_3; }
      if (*this == KeyCode::U)            { *this = KeyCode::KEYPAD_4; }
      if (*this == KeyCode::I)            { *this = KeyCode::KEYPAD_5; }
      if (*this == KeyCode::O)            { *this = KeyCode::KEYPAD_6; }
      if (*this == KeyCode::KEY_7)        { *this = KeyCode::KEYPAD_7; }
      if (*this == KeyCode::KEY_8)        { *this = KeyCode::KEYPAD_8; }
      if (*this == KeyCode::KEY_9)        { *this = KeyCode::KEYPAD_9; }
      if (*this == KeyCode::KEY_6)        { *this = KeyCode::KEYPAD_CLEAR; }
      if (*this == KeyCode::SLASH)        { *this = KeyCode::KEYPAD_PLUS; }
      if (*this == KeyCode::SEMICOLON)    { *this = KeyCode::KEYPAD_MINUS; }
      if (*this == KeyCode::P)            { *this = KeyCode::KEYPAD_MULTIPLY; }
      if (*this == KeyCode::KEY_0)        { *this = KeyCode::KEYPAD_SLASH; }
      if (*this == KeyCode::MINUS)        { *this = KeyCode::KEYPAD_EQUAL; }
      if (*this == KeyCode::DOT)          { *this = KeyCode::KEYPAD_DOT; }
      if (*this == KeyCode::CURSOR_UP)    { *this = KeyCode::PAGEUP; }
      if (*this == KeyCode::CURSOR_DOWN)  { *this = KeyCode::PAGEDOWN; }
      if (*this == KeyCode::CURSOR_LEFT)  { *this = KeyCode::HOME; }
      if (*this == KeyCode::CURSOR_RIGHT) { *this = KeyCode::END; }
      if (*this == KeyCode::RETURN)       { *this = KeyCode::ENTER; }
      if (*this == KeyCode::DELETE)       { *this = KeyCode::FORWARD_DELETE; }
    }
    if (keyboardType == KeyboardType::POWERBOOK ||
        keyboardType == KeyboardType::POWERBOOK_G4 ||
        keyboardType == KeyboardType::POWERBOOK_G4_TI) {
      if (*this == KeyCode::ENTER) { *this = KeyCode::ENTER_POWERBOOK; }
    }

    // ------------------------------------------------------------
    // set ModifierFlag::KEYPAD, ModifierFlag::CURSOR
    flags.stripCURSOR().stripKEYPAD();

    // Note: KEYPAD_CLEAR, KEYPAD_COMMA have no ModifierFlag::KEYPAD bit.
    if (*this == KeyCode::KEYPAD_0 || *this == KeyCode::KEYPAD_1 || *this == KeyCode::KEYPAD_2 ||
        *this == KeyCode::KEYPAD_3 || *this == KeyCode::KEYPAD_4 || *this == KeyCode::KEYPAD_5 ||
        *this == KeyCode::KEYPAD_6 || *this == KeyCode::KEYPAD_7 || *this == KeyCode::KEYPAD_8 ||
        *this == KeyCode::KEYPAD_9 ||
        *this == KeyCode::KEYPAD_DOT ||
        *this == KeyCode::KEYPAD_MULTIPLY ||
        *this == KeyCode::KEYPAD_PLUS ||
        *this == KeyCode::KEYPAD_SLASH ||
        *this == KeyCode::KEYPAD_MINUS ||
        *this == KeyCode::KEYPAD_EQUAL) {
      flags.add(ModifierFlag::KEYPAD);
    }

    if (*this == KeyCode::CURSOR_UP ||
        *this == KeyCode::CURSOR_DOWN ||
        *this == KeyCode::CURSOR_LEFT ||
        *this == KeyCode::CURSOR_RIGHT) {
      flags.add(ModifierFlag::CURSOR);
    }
  }

  const KeyCode&
  ModifierFlag::getKeyCode(void) const {
    if (*this == ModifierFlag::CAPSLOCK) return KeyCode::CAPSLOCK;
    if (*this == ModifierFlag::SHIFT_L) return KeyCode::SHIFT_L;
    if (*this == ModifierFlag::SHIFT_R) return KeyCode::SHIFT_R;
    if (*this == ModifierFlag::CONTROL_L) return KeyCode::CONTROL_L;
    if (*this == ModifierFlag::CONTROL_R) return KeyCode::CONTROL_R;
    if (*this == ModifierFlag::OPTION_L) return KeyCode::OPTION_L;
    if (*this == ModifierFlag::OPTION_R) return KeyCode::OPTION_R;
    if (*this == ModifierFlag::COMMAND_L) return KeyCode::COMMAND_L;
    if (*this == ModifierFlag::COMMAND_R) return KeyCode::COMMAND_R;
    if (*this == ModifierFlag::FN) return KeyCode::FN;

    return KeyCode::VK_NONE;
  }

  ModifierFlag
  KeyCode::getModifierFlag(void) const
  {
    if (*this == KeyCode::CAPSLOCK) return ModifierFlag::CAPSLOCK;
    if (*this == KeyCode::SHIFT_L) return ModifierFlag::SHIFT_L;
    if (*this == KeyCode::SHIFT_R) return ModifierFlag::SHIFT_R;
    if (*this == KeyCode::CONTROL_L) return ModifierFlag::CONTROL_L;
    if (*this == KeyCode::CONTROL_R) return ModifierFlag::CONTROL_R;
    if (*this == KeyCode::OPTION_L) return ModifierFlag::OPTION_L;
    if (*this == KeyCode::OPTION_R) return ModifierFlag::OPTION_R;
    if (*this == KeyCode::COMMAND_L) return ModifierFlag::COMMAND_L;
    if (*this == KeyCode::COMMAND_R) return ModifierFlag::COMMAND_R;
    if (*this == KeyCode::FN) return ModifierFlag::FN;

    return ModifierFlag::NONE;
  }
}
<commit_msg>fix KeyCode::normalizeKey @ kext<commit_after>#include "KeyCode.hpp"

namespace org_pqrs_KeyRemap4MacBook {
#include "keycode/output/include.EventType.cpp"
#include "keycode/output/include.KeyboardType.cpp"
#include "keycode/output/include.ModifierFlag.cpp"
#include "keycode/output/include.KeyCode.cpp"
#include "keycode/output/include.ConsumerKeyCode.cpp"
#include "keycode/output/include.PointingButton.cpp"

  // ------------------------------------------------------------
  bool
  EventType::isKeyDownOrModifierDown(const KeyCode& key, const Flags& flags) const
  {
    if (*this == EventType::DOWN) return true;
    if (*this == EventType::MODIFY) {
      return flags.isOn(key.getModifierFlag());
    }
    return false;
  }

  void
  KeyCode::normalizeKey(Flags& flags, const KeyboardType& keyboardType)
  {
    if (keyboardType == KeyboardType::POWERBOOK ||
        keyboardType == KeyboardType::POWERBOOK_G4 ||
        keyboardType == KeyboardType::POWERBOOK_G4_TI) {
      if (*this == KeyCode::ENTER_POWERBOOK) { *this = KeyCode::ENTER; }
    }

    if (flags.isOn(ModifierFlag::FN)) {
      // Note: KEYPAD_CLEAR has no ModifierFlag::KEYPAD bit.
      if (*this == KeyCode::KEYPAD_0)        { *this = KeyCode::M;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_1)        { *this = KeyCode::J;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_2)        { *this = KeyCode::K;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_3)        { *this = KeyCode::L;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_4)        { *this = KeyCode::U;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_5)        { *this = KeyCode::I;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_6)        { *this = KeyCode::O;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_7)        { *this = KeyCode::KEY_7;        flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_8)        { *this = KeyCode::KEY_8;        flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_9)        { *this = KeyCode::KEY_9;        flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_CLEAR)    { *this = KeyCode::KEY_6; }
      if (*this == KeyCode::KEYPAD_PLUS)     { *this = KeyCode::SLASH;        flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_MINUS)    { *this = KeyCode::SEMICOLON;    flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_MULTIPLY) { *this = KeyCode::P;            flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_SLASH)    { *this = KeyCode::KEY_0;        flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_EQUAL)    { *this = KeyCode::MINUS;        flags.stripKEYPAD(); }
      if (*this == KeyCode::KEYPAD_DOT)      { *this = KeyCode::DOT;          flags.stripKEYPAD(); }
      if (*this == KeyCode::PAGEUP)          { *this = KeyCode::CURSOR_UP;    flags.add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::PAGEDOWN)        { *this = KeyCode::CURSOR_DOWN;  flags.add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::HOME)            { *this = KeyCode::CURSOR_LEFT;  flags.add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::END)             { *this = KeyCode::CURSOR_RIGHT; flags.add(ModifierFlag::CURSOR); }
      if (*this == KeyCode::ENTER)           { *this = KeyCode::RETURN; }
      if (*this == KeyCode::FORWARD_DELETE)  { *this = KeyCode::DELETE; }
    }
  }

  void
  KeyCode::reverseNormalizeKey(Flags& flags, const KeyboardType& keyboardType)
  {
    if (flags.isOn(ModifierFlag::FN)) {
      if (*this == KeyCode::M)            { *this = KeyCode::KEYPAD_0; }
      if (*this == KeyCode::J)            { *this = KeyCode::KEYPAD_1; }
      if (*this == KeyCode::K)            { *this = KeyCode::KEYPAD_2; }
      if (*this == KeyCode::L)            { *this = KeyCode::KEYPAD_3; }
      if (*this == KeyCode::U)            { *this = KeyCode::KEYPAD_4; }
      if (*this == KeyCode::I)            { *this = KeyCode::KEYPAD_5; }
      if (*this == KeyCode::O)            { *this = KeyCode::KEYPAD_6; }
      if (*this == KeyCode::KEY_7)        { *this = KeyCode::KEYPAD_7; }
      if (*this == KeyCode::KEY_8)        { *this = KeyCode::KEYPAD_8; }
      if (*this == KeyCode::KEY_9)        { *this = KeyCode::KEYPAD_9; }
      if (*this == KeyCode::KEY_6)        { *this = KeyCode::KEYPAD_CLEAR; }
      if (*this == KeyCode::SLASH)        { *this = KeyCode::KEYPAD_PLUS; }
      if (*this == KeyCode::SEMICOLON)    { *this = KeyCode::KEYPAD_MINUS; }
      if (*this == KeyCode::P)            { *this = KeyCode::KEYPAD_MULTIPLY; }
      if (*this == KeyCode::KEY_0)        { *this = KeyCode::KEYPAD_SLASH; }
      if (*this == KeyCode::MINUS)        { *this = KeyCode::KEYPAD_EQUAL; }
      if (*this == KeyCode::DOT)          { *this = KeyCode::KEYPAD_DOT; }
      if (*this == KeyCode::CURSOR_UP)    { *this = KeyCode::PAGEUP; }
      if (*this == KeyCode::CURSOR_DOWN)  { *this = KeyCode::PAGEDOWN; }
      if (*this == KeyCode::CURSOR_LEFT)  { *this = KeyCode::HOME; }
      if (*this == KeyCode::CURSOR_RIGHT) { *this = KeyCode::END; }
      if (*this == KeyCode::RETURN)       { *this = KeyCode::ENTER; }
      if (*this == KeyCode::DELETE)       { *this = KeyCode::FORWARD_DELETE; }
    }
    if (keyboardType == KeyboardType::POWERBOOK ||
        keyboardType == KeyboardType::POWERBOOK_G4 ||
        keyboardType == KeyboardType::POWERBOOK_G4_TI) {
      if (*this == KeyCode::ENTER) { *this = KeyCode::ENTER_POWERBOOK; }
    }

    // ------------------------------------------------------------
    // set ModifierFlag::KEYPAD, ModifierFlag::CURSOR
    flags.stripCURSOR().stripKEYPAD();

    // Note: KEYPAD_CLEAR, KEYPAD_COMMA have no ModifierFlag::KEYPAD bit.
    if (*this == KeyCode::KEYPAD_0 || *this == KeyCode::KEYPAD_1 || *this == KeyCode::KEYPAD_2 ||
        *this == KeyCode::KEYPAD_3 || *this == KeyCode::KEYPAD_4 || *this == KeyCode::KEYPAD_5 ||
        *this == KeyCode::KEYPAD_6 || *this == KeyCode::KEYPAD_7 || *this == KeyCode::KEYPAD_8 ||
        *this == KeyCode::KEYPAD_9 ||
        *this == KeyCode::KEYPAD_DOT ||
        *this == KeyCode::KEYPAD_MULTIPLY ||
        *this == KeyCode::KEYPAD_PLUS ||
        *this == KeyCode::KEYPAD_SLASH ||
        *this == KeyCode::KEYPAD_MINUS ||
        *this == KeyCode::KEYPAD_EQUAL) {
      flags.add(ModifierFlag::KEYPAD);
    }

    if (*this == KeyCode::CURSOR_UP ||
        *this == KeyCode::CURSOR_DOWN ||
        *this == KeyCode::CURSOR_LEFT ||
        *this == KeyCode::CURSOR_RIGHT) {
      flags.add(ModifierFlag::CURSOR);
    }
  }

  const KeyCode&
  ModifierFlag::getKeyCode(void) const {
    if (*this == ModifierFlag::CAPSLOCK) return KeyCode::CAPSLOCK;
    if (*this == ModifierFlag::SHIFT_L) return KeyCode::SHIFT_L;
    if (*this == ModifierFlag::SHIFT_R) return KeyCode::SHIFT_R;
    if (*this == ModifierFlag::CONTROL_L) return KeyCode::CONTROL_L;
    if (*this == ModifierFlag::CONTROL_R) return KeyCode::CONTROL_R;
    if (*this == ModifierFlag::OPTION_L) return KeyCode::OPTION_L;
    if (*this == ModifierFlag::OPTION_R) return KeyCode::OPTION_R;
    if (*this == ModifierFlag::COMMAND_L) return KeyCode::COMMAND_L;
    if (*this == ModifierFlag::COMMAND_R) return KeyCode::COMMAND_R;
    if (*this == ModifierFlag::FN) return KeyCode::FN;

    return KeyCode::VK_NONE;
  }

  ModifierFlag
  KeyCode::getModifierFlag(void) const
  {
    if (*this == KeyCode::CAPSLOCK) return ModifierFlag::CAPSLOCK;
    if (*this == KeyCode::SHIFT_L) return ModifierFlag::SHIFT_L;
    if (*this == KeyCode::SHIFT_R) return ModifierFlag::SHIFT_R;
    if (*this == KeyCode::CONTROL_L) return ModifierFlag::CONTROL_L;
    if (*this == KeyCode::CONTROL_R) return ModifierFlag::CONTROL_R;
    if (*this == KeyCode::OPTION_L) return ModifierFlag::OPTION_L;
    if (*this == KeyCode::OPTION_R) return ModifierFlag::OPTION_R;
    if (*this == KeyCode::COMMAND_L) return ModifierFlag::COMMAND_L;
    if (*this == KeyCode::COMMAND_R) return ModifierFlag::COMMAND_R;
    if (*this == KeyCode::FN) return ModifierFlag::FN;

    return ModifierFlag::NONE;
  }
}
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/queryeval/searchiterator.h>

#include <algorithm>

using namespace search::fef;

struct State {
    SimpleTermData          term;
    MatchData::UP           md;
    TermFieldMatchData     *f3;
    TermFieldMatchData     *f5;
    TermFieldMatchData     *f7;
    TermFieldMatchDataArray array;

    State();
    ~State();

    void setArray(TermFieldMatchDataArray value) {
        array = value;
    }
};

State::State() : term(), md(), f3(0), f5(0), f7(0), array() {}
State::~State() {}

void testInvalidId() {
    const TermFieldMatchData empty;
    using search::queryeval::SearchIterator;

    EXPECT_EQUAL(TermFieldMatchData::invalidId(), empty.getDocId());
    EXPECT_TRUE(TermFieldMatchData::invalidId() < (SearchIterator::beginId() + 1 ) ||
               TermFieldMatchData::invalidId() > (search::endDocId - 1));
}

void testSetup(State &state) {
    MatchDataLayout layout;

    state.term.addField(3); // docfreq = 1
    state.term.addField(7); // docfreq = 2
    state.term.addField(5); // docfreq = 3

    typedef search::fef::ITermFieldRangeAdapter FRA;
    typedef search::fef::SimpleTermFieldRangeAdapter SFR;

    // lookup terms
    {
        int i = 1;
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setDocFreq(0.25 * i++);
        }
    }

    // reserve handles
    {
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setHandle(layout.allocTermField(iter.get().getFieldId()));
        }
    }

    state.md = layout.createMatchData();

    // init match data
    {
        for (FRA iter(state.term); iter.valid(); iter.next()) {
            const ITermFieldData& tfd = iter.get();

            TermFieldHandle handle = tfd.getHandle();
            TermFieldMatchData *data = state.md->resolveTermField(handle);
            switch (tfd.getFieldId()) {
            case 3:
                state.f3 = data;
                break;
            case 5:
                state.f5 = data;
                break;
            case 7:
                state.f7 = data;
                break;
            default:
                EXPECT_TRUE(false);
            }
        }
        EXPECT_EQUAL(3u, state.f3->getFieldId());
        EXPECT_EQUAL(5u, state.f5->getFieldId());
        EXPECT_EQUAL(7u, state.f7->getFieldId());
    }

    // test that we can setup array
    EXPECT_EQUAL(false, state.array.valid());
    state.setArray(TermFieldMatchDataArray().add(state.f3).add(state.f5).add(state.f7));
    EXPECT_EQUAL(true, state.array.valid());
}

void testGenerate(State &state) {
    // verify array
    EXPECT_EQUAL(3u, state.array.size());
    EXPECT_EQUAL(state.f3, state.array[0]);
    EXPECT_EQUAL(state.f5, state.array[1]);
    EXPECT_EQUAL(state.f7, state.array[2]);

    // stale unpacked data
    state.f5->reset(5);
    EXPECT_EQUAL(5u, state.f5->getDocId());
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        state.f5->appendPosition(pos);
        EXPECT_EQUAL(1u, state.f5->getIterator().size());
        EXPECT_EQUAL(10u, state.f5->getIterator().getFieldLength());
    }
    state.f5->reset(6);
    EXPECT_EQUAL(6u, state.f5->getDocId());
    EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
               state.f5->getIterator().getFieldLength());
    EXPECT_EQUAL(0u, state.f5->getIterator().size());

    // fresh unpacked data
    state.f3->reset(10);
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
                   state.f3->getIterator().getFieldLength());
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(10u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(15);
        pos.setElementId(1);
        pos.setElementLen(20);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(1);
        pos.setElementId(2);
        pos.setElementLen(5);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }

    // raw score
    state.f7->setRawScore(10, 5.0);
}

void testAnalyze(State &state) {
    EXPECT_EQUAL(10u, state.f3->getDocId());
    EXPECT_NOT_EQUAL(10u, state.f5->getDocId());
    EXPECT_EQUAL(10u, state.f7->getDocId());

    FieldPositionsIterator it = state.f3->getIterator();
    EXPECT_EQUAL(20u, it.getFieldLength());
    EXPECT_EQUAL(3u, it.size());
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(3u, it.getPosition());
    EXPECT_EQUAL(0u, it.getElementId());
    EXPECT_EQUAL(10u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(15u, it.getPosition());
    EXPECT_EQUAL(1u, it.getElementId());
    EXPECT_EQUAL(20u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(1u, it.getPosition());
    EXPECT_EQUAL(2u, it.getElementId());
    EXPECT_EQUAL(5u, it.getElementLen());
    it.next();
    EXPECT_TRUE(!it.valid());

    EXPECT_EQUAL(0.0, state.f3->getRawScore());
    EXPECT_EQUAL(0.0, state.f5->getRawScore());
    EXPECT_EQUAL(5.0, state.f7->getRawScore());
}

TEST("term field model") {
    State state;
    testSetup(state);
    testGenerate(state);
    testAnalyze(state);
    testInvalidId();
}

TEST("append positions") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    tfmd.reset(7);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos(0x01020304, 0x10203040, 0x11223344, 0x12345678);
    tfmd.appendPosition(pos);
    EXPECT_EQUAL(1u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());
    tfmd.reset(11);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos2(0x21020304, 0x20203040, 0x21223344, 0x22345678);
    tfmd.appendPosition(pos);
    tfmd.appendPosition(pos2);
    EXPECT_EQUAL(2u, tfmd.size());
    EXPECT_EQUAL(2u, tfmd.capacity());
    TermFieldMatchDataPosition pos3(0x31020304, 0x30203040, 0x31223344, 0x32345678);
    tfmd.appendPosition(pos3);
    EXPECT_EQUAL(3u, tfmd.size());
    EXPECT_EQUAL(4u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());

    EXPECT_EQUAL(0x21020304u, tfmd.begin()[1].getElementId());
    EXPECT_EQUAL(0x20203040u, tfmd.begin()[1].getPosition());
    EXPECT_EQUAL(0x21223344, tfmd.begin()[1].getElementWeight());
    EXPECT_EQUAL(0x22345678u, tfmd.begin()[1].getElementLen());

    EXPECT_EQUAL(0x31020304u, tfmd.begin()[2].getElementId());
    EXPECT_EQUAL(0x30203040u, tfmd.begin()[2].getPosition());
    EXPECT_EQUAL(0x31223344, tfmd.begin()[2].getElementWeight());
    EXPECT_EQUAL(0x32345678u, tfmd.begin()[2].getElementLen());
}

TEST("Access subqueries") {
    State state;
    testSetup(state);
    state.f3->reset(10);
    state.f3->setSubqueries(10, 42);
    EXPECT_EQUAL(42ULL, state.f3->getSubqueries());
    state.f3->enableRawScore();
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());

    state.f3->reset(11);
    state.f3->appendPosition(TermFieldMatchDataPosition());
    state.f3->setSubqueries(11, 42);
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());
}

TEST("require that TermFieldMatchData can be tagged as needed or not") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    tfmd.tagAsNotNeeded();
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(tfmd.isNotNeeded());
    tfmd.tagAsNeeded();
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
}

TEST("require that MatchData soft_reset retains appropriate state") {
    auto md = MatchData::makeTestInstance(10, 10);
    md->set_termwise_limit(0.5);
    auto *old_term = md->resolveTermField(7);
    old_term->tagAsNotNeeded();
    old_term->populate_fixed()->setElementWeight(21);
    old_term->resetOnlyDocId(42);
    EXPECT_EQUAL(md->get_termwise_limit(), 0.5);
    EXPECT_TRUE(old_term->isNotNeeded());
    EXPECT_EQUAL(old_term->getFieldId(), 7u);
    EXPECT_EQUAL(old_term->getWeight(), 21);
    EXPECT_EQUAL(old_term->getDocId(), 42u);
    md->soft_reset();
    auto *new_term = md->resolveTermField(7);
    EXPECT_EQUAL(new_term, old_term);
    EXPECT_EQUAL(md->get_termwise_limit(), 1.0);
    EXPECT_TRUE(!new_term->isNotNeeded());
    EXPECT_EQUAL(new_term->getFieldId(), 7u);
    EXPECT_EQUAL(new_term->getWeight(), 21);
    EXPECT_EQUAL(new_term->getDocId(), TermFieldMatchData::invalidId());
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>add test<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/queryeval/searchiterator.h>

#include <algorithm>

using namespace search::fef;

struct State {
    SimpleTermData          term;
    MatchData::UP           md;
    TermFieldMatchData     *f3;
    TermFieldMatchData     *f5;
    TermFieldMatchData     *f7;
    TermFieldMatchDataArray array;

    State();
    ~State();

    void setArray(TermFieldMatchDataArray value) {
        array = value;
    }
};

State::State() : term(), md(), f3(0), f5(0), f7(0), array() {}
State::~State() {}

void testInvalidId() {
    const TermFieldMatchData empty;
    using search::queryeval::SearchIterator;

    EXPECT_EQUAL(TermFieldMatchData::invalidId(), empty.getDocId());
    EXPECT_TRUE(TermFieldMatchData::invalidId() < (SearchIterator::beginId() + 1 ) ||
               TermFieldMatchData::invalidId() > (search::endDocId - 1));
}

void testSetup(State &state) {
    MatchDataLayout layout;

    state.term.addField(3); // docfreq = 1
    state.term.addField(7); // docfreq = 2
    state.term.addField(5); // docfreq = 3

    typedef search::fef::ITermFieldRangeAdapter FRA;
    typedef search::fef::SimpleTermFieldRangeAdapter SFR;

    // lookup terms
    {
        int i = 1;
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setDocFreq(0.25 * i++);
        }
    }

    // reserve handles
    {
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setHandle(layout.allocTermField(iter.get().getFieldId()));
        }
    }

    state.md = layout.createMatchData();

    // init match data
    {
        for (FRA iter(state.term); iter.valid(); iter.next()) {
            const ITermFieldData& tfd = iter.get();

            TermFieldHandle handle = tfd.getHandle();
            TermFieldMatchData *data = state.md->resolveTermField(handle);
            switch (tfd.getFieldId()) {
            case 3:
                state.f3 = data;
                break;
            case 5:
                state.f5 = data;
                break;
            case 7:
                state.f7 = data;
                break;
            default:
                EXPECT_TRUE(false);
            }
        }
        EXPECT_EQUAL(3u, state.f3->getFieldId());
        EXPECT_EQUAL(5u, state.f5->getFieldId());
        EXPECT_EQUAL(7u, state.f7->getFieldId());
    }

    // test that we can setup array
    EXPECT_EQUAL(false, state.array.valid());
    state.setArray(TermFieldMatchDataArray().add(state.f3).add(state.f5).add(state.f7));
    EXPECT_EQUAL(true, state.array.valid());
}

void testGenerate(State &state) {
    // verify array
    EXPECT_EQUAL(3u, state.array.size());
    EXPECT_EQUAL(state.f3, state.array[0]);
    EXPECT_EQUAL(state.f5, state.array[1]);
    EXPECT_EQUAL(state.f7, state.array[2]);

    // stale unpacked data
    state.f5->reset(5);
    EXPECT_EQUAL(5u, state.f5->getDocId());
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        state.f5->appendPosition(pos);
        EXPECT_EQUAL(1u, state.f5->getIterator().size());
        EXPECT_EQUAL(10u, state.f5->getIterator().getFieldLength());
    }
    state.f5->reset(6);
    EXPECT_EQUAL(6u, state.f5->getDocId());
    EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
               state.f5->getIterator().getFieldLength());
    EXPECT_EQUAL(0u, state.f5->getIterator().size());

    // fresh unpacked data
    state.f3->reset(10);
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
                   state.f3->getIterator().getFieldLength());
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(10u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(15);
        pos.setElementId(1);
        pos.setElementLen(20);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(1);
        pos.setElementId(2);
        pos.setElementLen(5);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }

    // raw score
    state.f7->setRawScore(10, 5.0);
}

void testAnalyze(State &state) {
    EXPECT_EQUAL(10u, state.f3->getDocId());
    EXPECT_NOT_EQUAL(10u, state.f5->getDocId());
    EXPECT_EQUAL(10u, state.f7->getDocId());

    FieldPositionsIterator it = state.f3->getIterator();
    EXPECT_EQUAL(20u, it.getFieldLength());
    EXPECT_EQUAL(3u, it.size());
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(3u, it.getPosition());
    EXPECT_EQUAL(0u, it.getElementId());
    EXPECT_EQUAL(10u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(15u, it.getPosition());
    EXPECT_EQUAL(1u, it.getElementId());
    EXPECT_EQUAL(20u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(1u, it.getPosition());
    EXPECT_EQUAL(2u, it.getElementId());
    EXPECT_EQUAL(5u, it.getElementLen());
    it.next();
    EXPECT_TRUE(!it.valid());

    EXPECT_EQUAL(0.0, state.f3->getRawScore());
    EXPECT_EQUAL(0.0, state.f5->getRawScore());
    EXPECT_EQUAL(5.0, state.f7->getRawScore());
}

TEST("term field model") {
    State state;
    testSetup(state);
    testGenerate(state);
    testAnalyze(state);
    testInvalidId();
}

TEST("append positions") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    tfmd.reset(7);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos(0x01020304, 0x10203040, 0x11223344, 0x12345678);
    tfmd.appendPosition(pos);
    EXPECT_EQUAL(1u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());
    tfmd.reset(11);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos2(0x21020304, 0x20203040, 0x21223344, 0x22345678);
    tfmd.appendPosition(pos);
    tfmd.appendPosition(pos2);
    EXPECT_EQUAL(2u, tfmd.size());
    EXPECT_EQUAL(2u, tfmd.capacity());
    TermFieldMatchDataPosition pos3(0x31020304, 0x30203040, 0x31223344, 0x32345678);
    tfmd.appendPosition(pos3);
    EXPECT_EQUAL(3u, tfmd.size());
    EXPECT_EQUAL(4u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());

    EXPECT_EQUAL(0x21020304u, tfmd.begin()[1].getElementId());
    EXPECT_EQUAL(0x20203040u, tfmd.begin()[1].getPosition());
    EXPECT_EQUAL(0x21223344, tfmd.begin()[1].getElementWeight());
    EXPECT_EQUAL(0x22345678u, tfmd.begin()[1].getElementLen());

    EXPECT_EQUAL(0x31020304u, tfmd.begin()[2].getElementId());
    EXPECT_EQUAL(0x30203040u, tfmd.begin()[2].getPosition());
    EXPECT_EQUAL(0x31223344, tfmd.begin()[2].getElementWeight());
    EXPECT_EQUAL(0x32345678u, tfmd.begin()[2].getElementLen());
}

TEST("Access subqueries") {
    State state;
    testSetup(state);
    state.f3->reset(10);
    state.f3->setSubqueries(10, 42);
    EXPECT_EQUAL(42ULL, state.f3->getSubqueries());
    state.f3->enableRawScore();
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());

    state.f3->reset(11);
    state.f3->appendPosition(TermFieldMatchDataPosition());
    state.f3->setSubqueries(11, 42);
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());
}

TEST("require that TermFieldMatchData can be tagged as needed or not") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    tfmd.tagAsNotNeeded();
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(tfmd.isNotNeeded());
    tfmd.tagAsNeeded();
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
}

TEST("require that MatchData soft_reset retains appropriate state") {
    auto md = MatchData::makeTestInstance(10, 10);
    md->set_termwise_limit(0.5);
    auto *old_term = md->resolveTermField(7);
    old_term->tagAsNotNeeded();
    old_term->populate_fixed()->setElementWeight(21);
    old_term->resetOnlyDocId(42);
    EXPECT_EQUAL(md->get_termwise_limit(), 0.5);
    EXPECT_TRUE(old_term->isNotNeeded());
    EXPECT_EQUAL(old_term->getFieldId(), 7u);
    EXPECT_EQUAL(old_term->getWeight(), 21);
    EXPECT_EQUAL(old_term->getDocId(), 42u);
    md->soft_reset();
    auto *new_term = md->resolveTermField(7);
    EXPECT_EQUAL(new_term, old_term);
    EXPECT_EQUAL(md->get_termwise_limit(), 1.0);
    EXPECT_TRUE(!new_term->isNotNeeded());
    EXPECT_EQUAL(new_term->getFieldId(), 7u);
    EXPECT_EQUAL(new_term->getWeight(), 21);
    EXPECT_EQUAL(new_term->getDocId(), TermFieldMatchData::invalidId());
}

TEST("require that compareWithExactness implements a strict weak ordering") {
   TermFieldMatchDataPosition a(0, 1, 100, 1);
   TermFieldMatchDataPosition b(0, 2, 100, 1);
   TermFieldMatchDataPosition c(0, 2, 100, 1);
   TermFieldMatchDataPosition d(0, 3, 100, 3);
   TermFieldMatchDataPosition e(0, 3, 100, 3);
   TermFieldMatchDataPosition f(0, 4, 100, 1);

   d.setMatchExactness(0.75);
   e.setMatchExactness(0.5);

   bool (*cmp)(const TermFieldMatchDataPosition &a,
               const TermFieldMatchDataPosition &b) = TermFieldMatchDataPosition::compareWithExactness;

   EXPECT_EQUAL(true, cmp(a, b));
   EXPECT_EQUAL(false, cmp(b, c));
   EXPECT_EQUAL(true, cmp(c, d));
   EXPECT_EQUAL(true, cmp(d, e));
   EXPECT_EQUAL(true, cmp(e, f));

   EXPECT_EQUAL(false, cmp(b, a));
   EXPECT_EQUAL(false, cmp(c, b));
   EXPECT_EQUAL(false, cmp(d, c));
   EXPECT_EQUAL(false, cmp(e, d));
   EXPECT_EQUAL(false, cmp(f, e));
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/searchlib/queryeval/dot_product_search.h>
#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/query/tree/simplequery.h>
#include <vespa/searchlib/queryeval/field_spec.h>
#include <vespa/searchlib/queryeval/blueprint.h>
#include <vespa/searchlib/queryeval/emptysearch.h>
#include <vespa/searchlib/queryeval/fake_result.h>
#include <vespa/searchlib/queryeval/fake_searchable.h>
#include <vespa/searchlib/queryeval/fake_requestcontext.h>
#include <vespa/searchlib/test/weightedchildrenverifiers.h>

using namespace search;
using namespace search::query;
using namespace search::fef;
using namespace search::queryeval;
using search::test::SearchIteratorVerifier;
using search::test::DocumentWeightAttributeHelper;

namespace {

void setupFakeSearchable(FakeSearchable &fake) {
    for (size_t docid = 1; docid < 10; ++docid) {
        std::string token1 = vespalib::make_string("%zu", docid);
        std::string token2 = vespalib::make_string("1%zu", docid);
        std::string token3 = vespalib::make_string("2%zu", docid);

        fake.addResult("field", token1, FakeResult().doc(docid).weight(docid).pos(0));
        fake.addResult("multi-field", token1, FakeResult().doc(docid).weight(docid).pos(0));
        fake.addResult("multi-field", token2, FakeResult().doc(docid).weight(2 * docid).pos(0));
        fake.addResult("multi-field", token3, FakeResult().doc(docid).weight(3 * docid).pos(0));
    }
}

struct DP {
    static const uint32_t fieldId = 0;
    static const TermFieldHandle handle = 0;
    std::vector<std::pair<std::string, uint32_t> > tokens;
    bool field_is_filter;
    bool term_is_not_needed;

    DP()
        : tokens(),
          field_is_filter(false),
          term_is_not_needed(false)
    {
    }

    DP &add(const std::string &token, uint32_t weight) {
        tokens.push_back(std::make_pair(token, weight));
        return *this;
    }
    DP& set_field_is_filter(bool value) { field_is_filter = value; return *this; }
    DP& set_term_is_not_needed(bool value) { term_is_not_needed = value; return *this; }

    Node::UP createNode() const {
        SimpleDotProduct *node = new SimpleDotProduct(tokens.size(), "view", 0, Weight(0));
        for (size_t i = 0; i < tokens.size(); ++i) {
            node->addTerm(tokens[i].first, Weight(tokens[i].second));
        }
        return Node::UP(node);
    }

    FakeResult search(Searchable &searchable, const std::string &field, bool strict) const {
        MatchData::UP md(MatchData::makeTestInstance(1, 1));
        if (term_is_not_needed) {
            md->resolveTermField(handle)->tagAsNotNeeded();
        }
        FakeRequestContext requestContext;
        Node::UP node = createNode();
        FieldSpecList fields = FieldSpecList().add(FieldSpec(field, fieldId, handle, field_is_filter));
        queryeval::Blueprint::UP bp = searchable.createBlueprint(requestContext, fields, *node);
        bp->fetchPostings(ExecuteInfo::create(strict));
        SearchIterator::UP sb = bp->createSearch(*md, strict);
        EXPECT_TRUE(dynamic_cast<DotProductSearch*>(sb.get()) != 0);
        sb->initFullRange();
        FakeResult result;
        for (uint32_t docId = 1; docId < 10; ++docId) {
            if (sb->seek(docId)) {
                sb->unpack(docId);
                result.doc(docId);
                double score = md->resolveTermField(handle)->getRawScore();
                EXPECT_EQUAL((int)score, score);
                result.score(score);
            }
        }
        return result;
    }
};

struct MockSearch : public SearchIterator {
    int seekCnt;
    uint32_t _initial;
    MockSearch(uint32_t initial) : SearchIterator(), seekCnt(0), _initial(initial) { }
    void initRange(uint32_t begin, uint32_t end) override {
        SearchIterator::initRange(begin, end);
        setDocId(_initial);
    }
    virtual void doSeek(uint32_t) override {
        ++seekCnt;
        setAtEnd();
    }
    virtual void doUnpack(uint32_t) override {}
};

struct MockFixture {
    MockSearch *mock;
    TermFieldMatchData tfmd;
    std::unique_ptr<SearchIterator> search;
    MockFixture(uint32_t initial) :
            MockFixture(initial, {new EmptySearch()})
    { }
    MockFixture(uint32_t initial, std::vector<SearchIterator *> children) : mock(0), tfmd(), search() {
        std::vector<TermFieldMatchData*> childMatch;
        std::vector<int32_t> weights;
        const size_t numChildren(children.size()+1);
        MatchData::UP md(MatchData::makeTestInstance(numChildren, numChildren));
        for (size_t i(0); i < children.size(); i++) {
            childMatch.push_back(md->resolveTermField(i));
            weights.push_back(1);
        }
        mock = new MockSearch(initial);
        childMatch.push_back(md->resolveTermField(children.size()));
        children.push_back(mock);
        weights.push_back(1);
        search = DotProductSearch::create(children, tfmd, false, childMatch, weights, std::move(md));
    }
};

    void verifySimple(const FakeResult & expect, DP & ws) {
        FakeSearchable index;
        setupFakeSearchable(index);
        EXPECT_EQUAL(expect, ws.search(index, "field", true));
        EXPECT_EQUAL(expect, ws.search(index, "field", false));
        EXPECT_EQUAL(expect, ws.search(index, "multi-field", true));
        EXPECT_EQUAL(expect, ws.search(index, "multi-field", false));
    }

} // namespace <unnamed>

std::function<int(int)>
make_score_filter(bool field_is_filter, bool term_is_not_needed)
{
    if (field_is_filter || term_is_not_needed) {
        return [](int) noexcept { return 0; };
    } else {
        return [](int value) noexcept { return value; };
    }
}

void run_simple(bool field_is_filter, bool term_is_not_needed)
{
    auto score_filter = make_score_filter(field_is_filter, term_is_not_needed);
    FakeResult expect = FakeResult()
                        .doc(3).score(score_filter(30 * 3))
                        .doc(5).score(score_filter(50 * 5))
                        .doc(7).score(score_filter(70 * 7));
    DP ws = DP().add("7", 70).add("5", 50).add("3", 30).add("100", 1000)
            .set_field_is_filter(field_is_filter)
            .set_term_is_not_needed(term_is_not_needed);

    TEST_DO(verifySimple(expect, ws));
}

TEST("test Simple") {
    TEST_DO(run_simple(false, false));
}

TEST("test Simple filter field") {
    TEST_DO(run_simple(true, false));
}

TEST("test Simple unranked") {
    TEST_DO(run_simple(false, true));
}

TEST("test Simple Single") {
    FakeResult expect = FakeResult()
            .doc(7).score(70 * 7);
    DP ws = DP().add("7", 70);

    TEST_DO(verifySimple(expect, ws));
}

void run_multi(bool field_is_filter, bool term_is_not_needed)
{
    auto score_filter = make_score_filter(field_is_filter, term_is_not_needed);
    FakeSearchable index;
    setupFakeSearchable(index);
    FakeResult expect = FakeResult()
                        .doc(3).score(score_filter(30 * 3 + 130 * 2 * 3 + 230 * 3 * 3))
                        .doc(5).score(score_filter(50 * 5 + 150 * 2 * 5))
                        .doc(7).score(score_filter(70 * 7));
    DP ws = DP().add("7", 70).add("5", 50).add("3", 30)
            .add("15", 150).add("13", 130)
            .add("23", 230).add("100", 1000)
            .set_field_is_filter(field_is_filter)
            .set_term_is_not_needed(term_is_not_needed);

    EXPECT_EQUAL(expect, ws.search(index, "multi-field", true));
    EXPECT_EQUAL(expect, ws.search(index, "multi-field", false));
}

TEST("test Multi") {
    TEST_DO(run_multi(false, false));
}

TEST("test Multi filter field") {
    TEST_DO(run_multi(true, false));
}
TEST("test Multi unranked") {
    TEST_DO(run_multi(false, true));
}

TEST_F("test Eager Empty Child", MockFixture(search::endDocId, {})) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_TRUE(search.isAtEnd());
    EXPECT_EQUAL(0, mock->seekCnt);
}

TEST_F("test Eager Empty Children", MockFixture(search::endDocId)) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_EQUAL(search.beginId(), search.getDocId());
    EXPECT_TRUE(!search.seek(1));
    EXPECT_TRUE(search.isAtEnd());
    EXPECT_EQUAL(0, mock->seekCnt);
}

void verifyEagerMatching(SearchIterator & search, MockSearch * mock) {
    EXPECT_TRUE(!search.seek(3));
    EXPECT_EQUAL(5u, search.getDocId());
    EXPECT_EQUAL(0, mock->seekCnt);
    EXPECT_TRUE(search.seek(5));
    EXPECT_EQUAL(5u, search.getDocId());
    EXPECT_EQUAL(0, mock->seekCnt);
    EXPECT_TRUE(!search.seek(7));
    EXPECT_TRUE(search.isAtEnd());
    EXPECT_EQUAL(1, mock->seekCnt);
}

TEST_F("test Eager Matching Child", MockFixture(5, {})) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_EQUAL(5u, search.getDocId());
    verifyEagerMatching(search, mock);
}

TEST_F("test Eager Matching Children", MockFixture(5)) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_EQUAL(search.beginId(), search.getDocId());
    verifyEagerMatching(search, mock);
}

class IteratorChildrenVerifier : public search::test::IteratorChildrenVerifier {
private:
    SearchIterator::UP create(const std::vector<SearchIterator*> &children) const override {
        std::vector<fef::TermFieldMatchData*> no_child_match;
        MatchData::UP no_match_data;
        return DotProductSearch::create(children, _tfmd, false, no_child_match, _weights, std::move(no_match_data));
    }
};

class WeightIteratorChildrenVerifier : public search::test::DwaIteratorChildrenVerifier {
private:
    SearchIterator::UP create(std::vector<DocumentWeightIterator> && children) const override {
        return SearchIterator::UP(DotProductSearch::create(_tfmd, false, _weights, std::move(children)));
    }
};

TEST("verify search iterator conformance with search iterator children") {
    IteratorChildrenVerifier verifier;
    verifier.verify();
}

TEST("verify search iterator conformance with document weight iterator children") {
    WeightIteratorChildrenVerifier verifier;
    verifier.verify();
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>Allow test to work with unpack too.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/searchlib/queryeval/dot_product_search.h>
#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/query/tree/simplequery.h>
#include <vespa/searchlib/queryeval/field_spec.h>
#include <vespa/searchlib/queryeval/blueprint.h>
#include <vespa/searchlib/queryeval/emptysearch.h>
#include <vespa/searchlib/queryeval/fake_result.h>
#include <vespa/searchlib/queryeval/fake_searchable.h>
#include <vespa/searchlib/queryeval/fake_requestcontext.h>
#include <vespa/searchlib/test/weightedchildrenverifiers.h>

using namespace search;
using namespace search::query;
using namespace search::fef;
using namespace search::queryeval;
using search::test::SearchIteratorVerifier;
using search::test::DocumentWeightAttributeHelper;

namespace {

void setupFakeSearchable(FakeSearchable &fake) {
    for (size_t docid = 1; docid < 10; ++docid) {
        std::string token1 = vespalib::make_string("%zu", docid);
        std::string token2 = vespalib::make_string("1%zu", docid);
        std::string token3 = vespalib::make_string("2%zu", docid);

        fake.addResult("field", token1, FakeResult().doc(docid).weight(docid).pos(0));
        fake.addResult("multi-field", token1, FakeResult().doc(docid).weight(docid).pos(0));
        fake.addResult("multi-field", token2, FakeResult().doc(docid).weight(2 * docid).pos(0));
        fake.addResult("multi-field", token3, FakeResult().doc(docid).weight(3 * docid).pos(0));
    }
}

struct DP {
    static const uint32_t fieldId = 0;
    static const TermFieldHandle handle = 0;
    std::vector<std::pair<std::string, uint32_t> > tokens;
    bool field_is_filter;
    bool term_is_not_needed;

    DP()
        : tokens(),
          field_is_filter(false),
          term_is_not_needed(false)
    {
    }

    DP &add(const std::string &token, uint32_t weight) {
        tokens.push_back(std::make_pair(token, weight));
        return *this;
    }
    DP& set_field_is_filter(bool value) { field_is_filter = value; return *this; }
    DP& set_term_is_not_needed(bool value) { term_is_not_needed = value; return *this; }

    Node::UP createNode() const {
        SimpleDotProduct *node = new SimpleDotProduct(tokens.size(), "view", 0, Weight(0));
        for (size_t i = 0; i < tokens.size(); ++i) {
            node->addTerm(tokens[i].first, Weight(tokens[i].second));
        }
        return Node::UP(node);
    }

    FakeResult search(Searchable &searchable, const std::string &field, bool strict) const {
        MatchData::UP md(MatchData::makeTestInstance(1, 1));
        if (term_is_not_needed) {
            md->resolveTermField(handle)->tagAsNotNeeded();
        }
        FakeRequestContext requestContext;
        Node::UP node = createNode();
        FieldSpecList fields = FieldSpecList().add(FieldSpec(field, fieldId, handle, field_is_filter));
        queryeval::Blueprint::UP bp = searchable.createBlueprint(requestContext, fields, *node);
        bp->fetchPostings(ExecuteInfo::create(strict));
        SearchIterator::UP sb = bp->createSearch(*md, strict);
        EXPECT_TRUE(dynamic_cast<DotProductSearch*>(sb.get()) != 0);
        sb->initFullRange();
        FakeResult result;
        for (uint32_t docId = 1; docId < 10; ++docId) {
            if (sb->seek(docId)) {
                sb->unpack(docId);
                result.doc(docId);
                double score = md->resolveTermField(handle)->getRawScore();
                EXPECT_EQUAL((int)score, score);
                result.score(score);
            }
        }
        return result;
    }
};

struct MockSearch : public SearchIterator {
    int seekCnt;
    uint32_t _initial;
    MockSearch(uint32_t initial) : SearchIterator(), seekCnt(0), _initial(initial) { }
    void initRange(uint32_t begin, uint32_t end) override {
        SearchIterator::initRange(begin, end);
        setDocId(_initial);
    }
    virtual void doSeek(uint32_t) override {
        ++seekCnt;
        setAtEnd();
    }
    virtual void doUnpack(uint32_t) override {}
};

struct MockFixture {
    MockSearch *mock;
    TermFieldMatchData tfmd;
    std::unique_ptr<SearchIterator> search;
    MockFixture(uint32_t initial) :
            MockFixture(initial, {new EmptySearch()})
    { }
    MockFixture(uint32_t initial, std::vector<SearchIterator *> children) : mock(0), tfmd(), search() {
        std::vector<TermFieldMatchData*> childMatch;
        std::vector<int32_t> weights;
        const size_t numChildren(children.size()+1);
        MatchData::UP md(MatchData::makeTestInstance(numChildren, numChildren));
        for (size_t i(0); i < children.size(); i++) {
            childMatch.push_back(md->resolveTermField(i));
            weights.push_back(1);
        }
        mock = new MockSearch(initial);
        childMatch.push_back(md->resolveTermField(children.size()));
        children.push_back(mock);
        weights.push_back(1);
        search = DotProductSearch::create(children, tfmd, false, childMatch, weights, std::move(md));
    }
};

    void verifySimple(const FakeResult & expect, DP & ws) {
        FakeSearchable index;
        setupFakeSearchable(index);
        EXPECT_EQUAL(expect, ws.search(index, "field", true));
        EXPECT_EQUAL(expect, ws.search(index, "field", false));
        EXPECT_EQUAL(expect, ws.search(index, "multi-field", true));
        EXPECT_EQUAL(expect, ws.search(index, "multi-field", false));
    }

} // namespace <unnamed>

std::function<int(int)>
make_score_filter(bool field_is_filter, bool term_is_not_needed)
{
    if (field_is_filter || term_is_not_needed) {
        return [](int) noexcept { return 0; };
    } else {
        return [](int value) noexcept { return value; };
    }
}

void run_simple(bool field_is_filter, bool term_is_not_needed)
{
    auto score_filter = make_score_filter(field_is_filter, term_is_not_needed);
    FakeResult expect = FakeResult()
                        .doc(3).score(score_filter(30 * 3))
                        .doc(5).score(score_filter(50 * 5))
                        .doc(7).score(score_filter(70 * 7));
    DP ws = DP().add("7", 70).add("5", 50).add("3", 30).add("100", 1000)
            .set_field_is_filter(field_is_filter)
            .set_term_is_not_needed(term_is_not_needed);

    TEST_DO(verifySimple(expect, ws));
}

TEST("test Simple") {
    TEST_DO(run_simple(false, false));
}

TEST("test Simple filter field") {
    TEST_DO(run_simple(true, false));
}

TEST("test Simple unranked") {
    TEST_DO(run_simple(false, true));
}

TEST("test Simple Single") {
    FakeResult expect = FakeResult()
            .doc(7).score(70 * 7);
    DP ws = DP().add("7", 70);

    TEST_DO(verifySimple(expect, ws));
}

void run_multi(bool field_is_filter, bool term_is_not_needed)
{
    auto score_filter = make_score_filter(field_is_filter, term_is_not_needed);
    FakeSearchable index;
    setupFakeSearchable(index);
    FakeResult expect = FakeResult()
                        .doc(3).score(score_filter(30 * 3 + 130 * 2 * 3 + 230 * 3 * 3))
                        .doc(5).score(score_filter(50 * 5 + 150 * 2 * 5))
                        .doc(7).score(score_filter(70 * 7));
    DP ws = DP().add("7", 70).add("5", 50).add("3", 30)
            .add("15", 150).add("13", 130)
            .add("23", 230).add("100", 1000)
            .set_field_is_filter(field_is_filter)
            .set_term_is_not_needed(term_is_not_needed);

    EXPECT_EQUAL(expect, ws.search(index, "multi-field", true));
    EXPECT_EQUAL(expect, ws.search(index, "multi-field", false));
}

TEST("test Multi") {
    TEST_DO(run_multi(false, false));
}

TEST("test Multi filter field") {
    TEST_DO(run_multi(true, false));
}
TEST("test Multi unranked") {
    TEST_DO(run_multi(false, true));
}

TEST_F("test Eager Empty Child", MockFixture(search::endDocId, {})) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_TRUE(search.isAtEnd());
    EXPECT_EQUAL(0, mock->seekCnt);
}

TEST_F("test Eager Empty Children", MockFixture(search::endDocId)) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_EQUAL(search.beginId(), search.getDocId());
    EXPECT_TRUE(!search.seek(1));
    EXPECT_TRUE(search.isAtEnd());
    EXPECT_EQUAL(0, mock->seekCnt);
}

void verifyEagerMatching(SearchIterator & search, MockSearch * mock) {
    EXPECT_TRUE(!search.seek(3));
    EXPECT_EQUAL(5u, search.getDocId());
    EXPECT_EQUAL(0, mock->seekCnt);
    EXPECT_TRUE(search.seek(5));
    EXPECT_EQUAL(5u, search.getDocId());
    EXPECT_EQUAL(0, mock->seekCnt);
    EXPECT_TRUE(!search.seek(7));
    EXPECT_TRUE(search.isAtEnd());
    EXPECT_EQUAL(1, mock->seekCnt);
}

TEST_F("test Eager Matching Child", MockFixture(5, {})) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_EQUAL(5u, search.getDocId());
    verifyEagerMatching(search, mock);
}

TEST_F("test Eager Matching Children", MockFixture(5)) {
    MockSearch *mock = f1.mock;
    SearchIterator &search = *f1.search;
    search.initFullRange();
    EXPECT_EQUAL(search.beginId(), search.getDocId());
    verifyEagerMatching(search, mock);
}

class IteratorChildrenVerifier : public search::test::IteratorChildrenVerifier {
private:
    SearchIterator::UP create(const std::vector<SearchIterator*> &children) const override {
        std::vector<fef::TermFieldMatchData*> no_child_match(children.size(), &_tfmd);
        MatchData::UP no_match_data;
        return DotProductSearch::create(children, _tfmd, false, no_child_match, _weights, std::move(no_match_data));
    }
};

class WeightIteratorChildrenVerifier : public search::test::DwaIteratorChildrenVerifier {
private:
    SearchIterator::UP create(std::vector<DocumentWeightIterator> && children) const override {
        return DotProductSearch::create(_tfmd, false, _weights, std::move(children));
    }
};

TEST("verify search iterator conformance with search iterator children") {
    IteratorChildrenVerifier verifier;
    verifier.verify();
}

TEST("verify search iterator conformance with document weight iterator children") {
    WeightIteratorChildrenVerifier verifier;
    verifier.verify();
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <stdint.h>

#include <initializer_list>
#include <tuple>
#include <vector>

#include <gtest/gtest.h>
#include "flatbuffers/flatbuffers.h"  // from @flatbuffers
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/schema/schema_generated.h"

namespace tflite {
namespace {

using ::testing::ElementsAreArray;

class ArgBaseOpModel : public SingleOpModel {
 public:
  ArgBaseOpModel(TensorType input_type, int axis_value, TensorType axis_type,
                 bool constant_axis, TensorType output_type)
      : axis_value_(axis_value),
        axis_type_(axis_type),
        constant_axis_(constant_axis) {
    input_ = AddInput(input_type);
    if (constant_axis) {
      if (axis_type == TensorType_INT64) {
        axis_ =
            AddConstInput(axis_type, {static_cast<int64_t>(axis_value)}, {1});
      } else {
        axis_ = AddConstInput(axis_type, {axis_value}, {1});
      }
    } else {
      axis_ = AddInput(axis_type);
    }
    output_ = AddOutput(output_type);
  }

  int input() const { return input_; }
  int axis() const { return axis_; }

  std::vector<int32_t> GetInt32Output() const {
    return ExtractVector<int32_t>(output_);
  }
  std::vector<int64_t> GetInt64Output() const {
    return ExtractVector<int64_t>(output_);
  }
  std::vector<int> GetOutputShape() { return GetTensorShape(output_); }

 protected:
  void PopulateAxisIfNeeded() {
    if (constant_axis_) return;
    if (axis_type_ == TensorType_INT32) {
      PopulateTensor<int32_t>(axis(), {axis_value_});
    } else {
      PopulateTensor<int64_t>(axis(), {axis_value_});
    }
  }

  const int axis_value_;
  const TensorType axis_type_;
  const bool constant_axis_;

  int input_;
  int axis_;
  int output_;
};

class ArgMaxOpModel : public ArgBaseOpModel {
 public:
  ArgMaxOpModel(std::initializer_list<int> input_shape, TensorType input_type,
                int axis_value, TensorType axis_type, bool constant_axis,
                TensorType output_type)
      : ArgBaseOpModel(input_type, axis_value, axis_type, constant_axis,
                       output_type) {
    ArgBaseOpModel::SetBuiltinOp(
        BuiltinOperator_ARG_MAX, BuiltinOptions_ArgMaxOptions,
        CreateArgMaxOptions(ArgBaseOpModel::builder_, output_type).Union());
    ArgBaseOpModel::BuildInterpreter({input_shape, {1}});
    PopulateAxisIfNeeded();
  }
};

class ArgMinOpModel : public ArgBaseOpModel {
 public:
  ArgMinOpModel(std::initializer_list<int> input_shape, TensorType input_type,
                int axis_value, TensorType axis_type, bool constant_axis,
                TensorType output_type)
      : ArgBaseOpModel(input_type, axis_value, axis_type, constant_axis,
                       output_type) {
    ArgBaseOpModel::SetBuiltinOp(
        BuiltinOperator_ARG_MIN, BuiltinOptions_ArgMinOptions,
        CreateArgMinOptions(ArgBaseOpModel::builder_, output_type).Union());
    ArgBaseOpModel::BuildInterpreter({input_shape, {1}});
    PopulateAxisIfNeeded();
  }
};

// Declare ArgMinMaxOpTest as a parameterized test, where the parameter is a
// tuple with:
// - boolean indicating whether to use a constant axis or not.
// - axis type (TensorType_INT32 or TensorType_INT64)
// - output type (TensorType_INT32 or TensorType_INT64)
class ArgMinMaxOpTest : public ::testing::TestWithParam<
                            std::tuple<bool, TensorType, TensorType>> {
 public:
  bool ConstantAxis() const { return std::get<0>(GetParam()); }

  TensorType AxisType() const { return std::get<1>(GetParam()); }

  TensorType OutputType() const { return std::get<2>(GetParam()); }

  void ValidateOutput(const ArgBaseOpModel& model,
                      const std::vector<int>& expected_output) {
    if (OutputType() == TensorType_INT32) {
      EXPECT_THAT(model.GetInt32Output(), ElementsAreArray(expected_output));
    } else {
      EXPECT_THAT(model.GetInt64Output(), ElementsAreArray(expected_output));
    }
  }
};

INSTANTIATE_TEST_SUITE_P(
    ArgMinMaxOpTest, ArgMinMaxOpTest,
    ::testing::Combine(::testing::Bool(),
                       ::testing::Values(TensorType_INT32, TensorType_INT64),
                       ::testing::Values(TensorType_INT32, TensorType_INT64)));

TEST_P(ArgMinMaxOpTest, GetMaxArgFloat) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_FLOAT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<float>(model.input(), {0.1, 0.9, 0.7, 0.3});
  model.Invoke();

  ValidateOutput(model, {1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgUInt8) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_UINT8, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<uint8_t>(model.input(), {1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgInt8) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_INT8, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int8_t>(model.input(), {-1, -9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {2});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgInt) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgMulDimensions) {
  ArgMaxOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {3, 1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgNegativeAxis) {
  ArgMaxOpModel model({1, 1, 2, 4}, TensorType_INT32, -2, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 1, 0, 0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 4}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgOutput64) {
  ArgMaxOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {10, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgFloat) {
  ArgMinOpModel model({1, 1, 1, 4}, TensorType_FLOAT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<float>(model.input(), {0.1, 0.9, 0.7, 0.3});
  model.Invoke();

  ValidateOutput(model, {0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgInt) {
  ArgMinOpModel model({1, 1, 1, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgMulDimensions) {
  ArgMinOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgNegativeAxis) {
  ArgMinOpModel model({1, 1, 2, 4}, TensorType_INT32, -2, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 0, 0, 1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 4}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgOutput64) {
  ArgMinOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {10, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {1, 0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

}  // namespace
}  // namespace tflite
<commit_msg>Add more tests about axis=dims-1. Iterate from 1 to 10 to ensure edge cases are correct.<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <stdint.h>

#include <initializer_list>
#include <tuple>
#include <vector>

#include <gtest/gtest.h>
#include "flatbuffers/flatbuffers.h"  // from @flatbuffers
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/schema/schema_generated.h"

namespace tflite {
namespace {

using ::testing::ElementsAreArray;

class ArgBaseOpModel : public SingleOpModel {
 public:
  ArgBaseOpModel(TensorType input_type, int axis_value, TensorType axis_type,
                 bool constant_axis, TensorType output_type)
      : axis_value_(axis_value),
        axis_type_(axis_type),
        constant_axis_(constant_axis) {
    input_ = AddInput(input_type);
    if (constant_axis) {
      if (axis_type == TensorType_INT64) {
        axis_ =
            AddConstInput(axis_type, {static_cast<int64_t>(axis_value)}, {1});
      } else {
        axis_ = AddConstInput(axis_type, {axis_value}, {1});
      }
    } else {
      axis_ = AddInput(axis_type);
    }
    output_ = AddOutput(output_type);
  }

  int input() const { return input_; }
  int axis() const { return axis_; }

  std::vector<int32_t> GetInt32Output() const {
    return ExtractVector<int32_t>(output_);
  }
  std::vector<int64_t> GetInt64Output() const {
    return ExtractVector<int64_t>(output_);
  }
  std::vector<int> GetOutputShape() { return GetTensorShape(output_); }

 protected:
  void PopulateAxisIfNeeded() {
    if (constant_axis_) return;
    if (axis_type_ == TensorType_INT32) {
      PopulateTensor<int32_t>(axis(), {axis_value_});
    } else {
      PopulateTensor<int64_t>(axis(), {axis_value_});
    }
  }

  const int axis_value_;
  const TensorType axis_type_;
  const bool constant_axis_;

  int input_;
  int axis_;
  int output_;
};

class ArgMaxOpModel : public ArgBaseOpModel {
 public:
  ArgMaxOpModel(std::initializer_list<int> input_shape, TensorType input_type,
                int axis_value, TensorType axis_type, bool constant_axis,
                TensorType output_type)
      : ArgBaseOpModel(input_type, axis_value, axis_type, constant_axis,
                       output_type) {
    ArgBaseOpModel::SetBuiltinOp(
        BuiltinOperator_ARG_MAX, BuiltinOptions_ArgMaxOptions,
        CreateArgMaxOptions(ArgBaseOpModel::builder_, output_type).Union());
    ArgBaseOpModel::BuildInterpreter({input_shape, {1}});
    PopulateAxisIfNeeded();
  }
};

class ArgMinOpModel : public ArgBaseOpModel {
 public:
  ArgMinOpModel(std::initializer_list<int> input_shape, TensorType input_type,
                int axis_value, TensorType axis_type, bool constant_axis,
                TensorType output_type)
      : ArgBaseOpModel(input_type, axis_value, axis_type, constant_axis,
                       output_type) {
    ArgBaseOpModel::SetBuiltinOp(
        BuiltinOperator_ARG_MIN, BuiltinOptions_ArgMinOptions,
        CreateArgMinOptions(ArgBaseOpModel::builder_, output_type).Union());
    ArgBaseOpModel::BuildInterpreter({input_shape, {1}});
    PopulateAxisIfNeeded();
  }
};

// Declare ArgMinMaxOpTest as a parameterized test, where the parameter is a
// tuple with:
// - boolean indicating whether to use a constant axis or not.
// - axis type (TensorType_INT32 or TensorType_INT64)
// - output type (TensorType_INT32 or TensorType_INT64)
class ArgMinMaxOpTest : public ::testing::TestWithParam<
                            std::tuple<bool, TensorType, TensorType>> {
 public:
  bool ConstantAxis() const { return std::get<0>(GetParam()); }

  TensorType AxisType() const { return std::get<1>(GetParam()); }

  TensorType OutputType() const { return std::get<2>(GetParam()); }

  void ValidateOutput(const ArgBaseOpModel& model,
                      const std::vector<int>& expected_output) {
    if (OutputType() == TensorType_INT32) {
      EXPECT_THAT(model.GetInt32Output(), ElementsAreArray(expected_output));
    } else {
      EXPECT_THAT(model.GetInt64Output(), ElementsAreArray(expected_output));
    }
  }
};

INSTANTIATE_TEST_SUITE_P(
    ArgMinMaxOpTest, ArgMinMaxOpTest,
    ::testing::Combine(::testing::Bool(),
                       ::testing::Values(TensorType_INT32, TensorType_INT64),
                       ::testing::Values(TensorType_INT32, TensorType_INT64)));

TEST_P(ArgMinMaxOpTest, GetMaxArgFloat) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_FLOAT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<float>(model.input(), {0.1, 0.9, 0.7, 0.3});
  model.Invoke();

  ValidateOutput(model, {1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgUInt8) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_UINT8, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<uint8_t>(model.input(), {1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgInt8) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_INT8, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int8_t>(model.input(), {-1, -9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {2});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgInt) {
  ArgMaxOpModel model({1, 1, 1, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgMulDimensions) {
  ArgMaxOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {3, 1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgNegativeAxis) {
  ArgMaxOpModel model({1, 1, 2, 4}, TensorType_INT32, -2, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 1, 0, 0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 4}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgOutput64) {
  ArgMaxOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {10, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

TEST_P(ArgMinMaxOpTest, GetMaxArgFloatLastAxis) {
  std::vector<float> input{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0};
  for (int i = 1; i < 10; ++i) {
    ArgMaxOpModel model({i}, TensorType_FLOAT32, 0, AxisType(), ConstantAxis(),
                        OutputType());
    model.PopulateTensor<float>(
        model.input(), std::vector<float>(input.begin(), input.begin() + i));
    model.Invoke();

    ValidateOutput(model, {i - 1});
  }
}

TEST_P(ArgMinMaxOpTest, GetMinArgFloat) {
  ArgMinOpModel model({1, 1, 1, 4}, TensorType_FLOAT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<float>(model.input(), {0.1, 0.9, 0.7, 0.3});
  model.Invoke();

  ValidateOutput(model, {0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgInt) {
  ArgMinOpModel model({1, 1, 1, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 1}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgMulDimensions) {
  ArgMinOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgNegativeAxis) {
  ArgMinOpModel model({1, 1, 2, 4}, TensorType_INT32, -2, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {1, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {0, 0, 0, 1});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 4}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgOutput64) {
  ArgMinOpModel model({1, 1, 2, 4}, TensorType_INT32, 3, AxisType(),
                      ConstantAxis(), OutputType());
  model.PopulateTensor<int>(model.input(), {10, 2, 7, 8, 1, 9, 7, 3});
  model.Invoke();

  ValidateOutput(model, {1, 0});
  EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 1, 2}));
}

TEST_P(ArgMinMaxOpTest, GetMinArgFloatLastAxis) {
  std::vector<float> input{1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1};
  for (int i = 1; i < 10; ++i) {
    ArgMinOpModel model({i}, TensorType_FLOAT32, 0, AxisType(), ConstantAxis(),
                        OutputType());
    model.PopulateTensor<float>(
        model.input(), std::vector<float>(input.begin(), input.begin() + i));
    model.Invoke();

    ValidateOutput(model, {i - 1});
  }
}

}  // namespace
}  // namespace tflite
<|endoftext|>


<commit_before>#ifndef RPT_XMLREPORTELEMENTBASE_HXX
#define RPT_XMLREPORTELEMENTBASE_HXX
/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: xmlReportElementBase.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: kz $ $Date: 2008-03-05 18:05:31 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _XMLOFF_XMLICTXT_HXX
#include <xmloff/xmlictxt.hxx>
#endif
#ifndef _COM_SUN_STAR_REPORT_XREPORTCOMPONENT_HPP_
#include <com/sun/star/report/XReportComponent.hpp>
#endif
#include <vector>

namespace rptxml
{
    class ORptFilter;
    class OXMLTable;

    class SAL_NO_VTABLE IMasterDetailFieds
    {
    public:
        virtual void addMasterDetailPair(const ::std::pair< ::rtl::OUString,::rtl::OUString >& _aPair) = 0;
    };

    class OXMLReportElementBase : public SvXMLImportContext
    {
        OXMLReportElementBase(const OXMLReportElementBase&);
        void operator =(const OXMLReportElementBase&);
    protected:
        ORptFilter&   m_rImport;
        OXMLTable*    m_pContainer;
        ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent >      m_xComponent;

        virtual SvXMLImportContext* _CreateChildContext( sal_uInt16 nPrefix,
                    const ::rtl::OUString& rLocalName,
                    const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList );
    public:

        OXMLReportElementBase( ORptFilter& rImport
                    ,sal_uInt16 nPrfx
                    ,const ::rtl::OUString& rLName
                    ,const ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent >& _xComponent
                    ,OXMLTable* _pContainer);
        virtual ~OXMLReportElementBase();

        virtual SvXMLImportContext *CreateChildContext( sal_uInt16 nPrefix,
                    const ::rtl::OUString& rLocalName,
                    const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList );

        virtual void EndElement();
    };
// -----------------------------------------------------------------------------
} // namespace rptxml
// -----------------------------------------------------------------------------

#endif // RPT_XMLREPORTELEMENTBASE_HXX
<commit_msg>INTEGRATION: CWS changefileheader (1.3.8); FILE MERGED 2008/04/01 15:23:38 thb 1.3.8.3: #i85898# Stripping all external header guards 2008/04/01 12:33:11 thb 1.3.8.2: #i85898# Stripping all external header guards 2008/03/31 13:32:10 rt 1.3.8.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: xmlReportElementBase.hxx,v $
 * $Revision: 1.4 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef RPT_XMLREPORTELEMENTBASE_HXX
#define RPT_XMLREPORTELEMENTBASE_HXX

#include <xmloff/xmlictxt.hxx>
#include <com/sun/star/report/XReportComponent.hpp>
#include <vector>

namespace rptxml
{
    class ORptFilter;
    class OXMLTable;

    class SAL_NO_VTABLE IMasterDetailFieds
    {
    public:
        virtual void addMasterDetailPair(const ::std::pair< ::rtl::OUString,::rtl::OUString >& _aPair) = 0;
    };

    class OXMLReportElementBase : public SvXMLImportContext
    {
        OXMLReportElementBase(const OXMLReportElementBase&);
        void operator =(const OXMLReportElementBase&);
    protected:
        ORptFilter&   m_rImport;
        OXMLTable*    m_pContainer;
        ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent >      m_xComponent;

        virtual SvXMLImportContext* _CreateChildContext( sal_uInt16 nPrefix,
                    const ::rtl::OUString& rLocalName,
                    const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList );
    public:

        OXMLReportElementBase( ORptFilter& rImport
                    ,sal_uInt16 nPrfx
                    ,const ::rtl::OUString& rLName
                    ,const ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent >& _xComponent
                    ,OXMLTable* _pContainer);
        virtual ~OXMLReportElementBase();

        virtual SvXMLImportContext *CreateChildContext( sal_uInt16 nPrefix,
                    const ::rtl::OUString& rLocalName,
                    const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList );

        virtual void EndElement();
    };
// -----------------------------------------------------------------------------
} // namespace rptxml
// -----------------------------------------------------------------------------

#endif // RPT_XMLREPORTELEMENTBASE_HXX
<|endoftext|>