@Test(dataProvider = "hashEnabledValues")
  public void testSingleChannel(boolean hashEnabled) throws Exception {
    List<Type> joinTypes = ImmutableList.<Type>of(VARCHAR);
    List<Integer> joinChannels = Ints.asList(0);

    // compile a single channel hash strategy
    PagesHashStrategyFactory pagesHashStrategyFactory =
        joinCompiler.compilePagesHashStrategyFactory(joinTypes, joinChannels);

    // create hash strategy with a single channel blocks -- make sure there is some overlap in
    // values
    List<Block> channel =
        ImmutableList.of(
            BlockAssertions.createStringSequenceBlock(10, 20),
            BlockAssertions.createStringSequenceBlock(20, 30),
            BlockAssertions.createStringSequenceBlock(15, 25));

    Optional<Integer> hashChannel = Optional.empty();
    List<List<Block>> channels = ImmutableList.of(channel);
    if (hashEnabled) {
      ImmutableList.Builder<Block> hashChannelBuilder = ImmutableList.builder();
      for (Block block : channel) {
        hashChannelBuilder.add(TypeUtils.getHashBlock(joinTypes, block));
      }
      hashChannel = Optional.of(1);
      channels = ImmutableList.of(channel, hashChannelBuilder.build());
    }
    PagesHashStrategy hashStrategy =
        pagesHashStrategyFactory.createPagesHashStrategy(channels, hashChannel);

    // verify channel count
    assertEquals(hashStrategy.getChannelCount(), 1);

    // verify hashStrategy is consistent with equals and hash code from block
    for (int leftBlockIndex = 0; leftBlockIndex < channel.size(); leftBlockIndex++) {
      Block leftBlock = channel.get(leftBlockIndex);

      PageBuilder pageBuilder = new PageBuilder(ImmutableList.of(VARCHAR));

      for (int leftBlockPosition = 0;
          leftBlockPosition < leftBlock.getPositionCount();
          leftBlockPosition++) {
        // hash code of position must match block hash
        assertEquals(
            hashStrategy.hashPosition(leftBlockIndex, leftBlockPosition),
            hashPosition(VARCHAR, leftBlock, leftBlockPosition));

        // position must be equal to itself
        assertTrue(
            hashStrategy.positionEqualsPositionIgnoreNulls(
                leftBlockIndex, leftBlockPosition, leftBlockIndex, leftBlockPosition));

        // check equality of every position against every other position in the block
        for (int rightBlockIndex = 0; rightBlockIndex < channel.size(); rightBlockIndex++) {
          Block rightBlock = channel.get(rightBlockIndex);
          for (int rightBlockPosition = 0;
              rightBlockPosition < rightBlock.getPositionCount();
              rightBlockPosition++) {
            boolean expected =
                positionEqualsPosition(
                    VARCHAR, leftBlock, leftBlockPosition, rightBlock, rightBlockPosition);
            assertEquals(
                hashStrategy.positionEqualsRow(
                    leftBlockIndex, leftBlockPosition, rightBlockPosition, new Page(rightBlock)),
                expected);
            assertEquals(
                hashStrategy.rowEqualsRow(
                    leftBlockPosition,
                    new Page(leftBlock),
                    rightBlockPosition,
                    new Page(rightBlock)),
                expected);
            assertEquals(
                hashStrategy.positionEqualsRowIgnoreNulls(
                    leftBlockIndex, leftBlockPosition, rightBlockPosition, new Page(rightBlock)),
                expected);
            assertEquals(
                hashStrategy.positionEqualsPositionIgnoreNulls(
                    leftBlockIndex, leftBlockPosition, rightBlockIndex, rightBlockPosition),
                expected);
            assertEquals(
                hashStrategy.positionEqualsPosition(
                    leftBlockIndex, leftBlockPosition, rightBlockIndex, rightBlockPosition),
                expected);
          }
        }

        // check equality of every position against every other position in the block cursor
        for (int rightBlockIndex = 0; rightBlockIndex < channel.size(); rightBlockIndex++) {
          Block rightBlock = channel.get(rightBlockIndex);
          for (int rightBlockPosition = 0;
              rightBlockPosition < rightBlock.getPositionCount();
              rightBlockPosition++) {
            boolean expected =
                positionEqualsPosition(
                    VARCHAR, leftBlock, leftBlockPosition, rightBlock, rightBlockPosition);
            assertEquals(
                hashStrategy.positionEqualsRow(
                    leftBlockIndex, leftBlockPosition, rightBlockPosition, new Page(rightBlock)),
                expected);
            assertEquals(
                hashStrategy.rowEqualsRow(
                    leftBlockPosition,
                    new Page(leftBlock),
                    rightBlockPosition,
                    new Page(rightBlock)),
                expected);
            assertEquals(
                hashStrategy.positionEqualsRowIgnoreNulls(
                    leftBlockIndex, leftBlockPosition, rightBlockPosition, new Page(rightBlock)),
                expected);
            assertEquals(
                hashStrategy.positionEqualsPositionIgnoreNulls(
                    leftBlockIndex, leftBlockPosition, rightBlockIndex, rightBlockPosition),
                expected);
            assertEquals(
                hashStrategy.positionEqualsPosition(
                    leftBlockIndex, leftBlockPosition, rightBlockIndex, rightBlockPosition),
                expected);
          }
        }

        // write position to output block
        pageBuilder.declarePosition();
        hashStrategy.appendTo(leftBlockIndex, leftBlockPosition, pageBuilder, 0);
      }

      // verify output block matches
      assertBlockEquals(VARCHAR, pageBuilder.build().getBlock(0), leftBlock);
    }
  }