[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/hhvm/hack-codegen/main/tests/HackBuilderTest.hack [Back]  [Original]

/*
 *  Copyright (c) 2015-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the MIT license found in the
 *  LICENSE file in the root directory of this source tree.
 *
 */

namespace Facebook\HackCodegen;

use namespace HH\Lib\Regex;

final class HackBuilderTest extends CodegenBaseTest {

  private function getHackBuilder(): HackBuilder {
    return $this->getCodegenFactory()->codegenHackBuilder();
  }

  public function testIfBlock(): void {
    $body = $this
      ->getHackBuilder()
      ->startIfBlockf('$value callSomeThingReallyLongName".
        'ReallyReallyLongName',
        Vector {
          '$someSmallParameter',
          "\$foobarbaz_alphabetagama +".
          $del.
          "\$foobarbaz_alphabetagamaa +".
          $del.
          "\$foobarbaz_alphabetagamatheta_foobarbaz",
        },
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testLiteralMap(): void {
    $body = $this
      ->getHackBuilder()
      ->addValue(
        Map {
          'MY_ENUM::A' => 'ANOTHER_ENUM::A',
          'MY_ENUM::B' => 'ANOTHER_ENUM::B',
        },
        HackBuilderValues::map(
          HackBuilderKeys::literal(),
          HackBuilderValues::literal(),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testAnotherConfig(): void {
    $body = (new HackBuilder(new TestAnotherCodegenConfig()))
      ->addInlineComment(
        'Here we wrap at 40 chars because we use a different configuration.',
      )
      ->startIfBlock('$do_that')
      ->add('return ')
      ->addValue(
        vec[1, 2, 3],
        HackBuilderValues::valueArray(HackBuilderValues::export()),
      )
      ->closeStatement()
      ->endIfBlock();

    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testSwitchBodyWithReturnsInCaseAndDefault(): void {
    // Gosh, I have no idea what names of football shots are!
    $players = Vector {
      dict['name' => 'Ronaldo', 'favorite_shot' => 'freeKick'],
      dict['name' => 'Messi', 'favorite_shot' => 'slideKick'],
      dict['name' => 'Maradona', 'favorite_shot' => 'handOfGod'],
    };

    $body = $this
      ->getHackBuilder()
      ->startSwitch('$soccer_player')
      ->addCaseBlocks(
        $players,
        ($player, $body) ==> {
          $body
            ->addCase($player['name'], HackBuilderValues::export())
            ->addLinef('$shot = new Shot(\'%s\');', $player['favorite_shot'])
            ->returnCasef('$shot->execute()');
        },
      )
      ->addDefault()
      ->addLine('invariant_violation(\'ball deflated!\');')
      ->endDefault()
      ->endSwitch();
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testSwitchBodyWithBreaksInCaseAndDefault(): void {
    // Gosh, I have no idea what names of football shots are!
    $players = Vector {
      dict['name' => 'Ronaldo', 'favorite_shot' => 'freeKick'],
      dict['name' => 'Messi', 'favorite_shot' => 'slideKick'],
      dict['name' => 'Maradona', 'favorite_shot' => 'handOfGod'],
    };

    $body = $this
      ->getHackBuilder()
      ->startSwitch('$soccer_player')
      ->addCaseBlocks(
        $players,
        ($player, $body) ==> {
          $body
            ->addCase($player['name'], HackBuilderValues::export())
            ->addLinef('$shot = new Shot(\'%s\');', $player['favorite_shot'])
            ->breakCase();
        },
      )
      ->addDefault()
      ->addLine('invariant_violation(\'ball deflated!\');')
      ->endDefault()
      ->endSwitch();
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testSwitchBodyWithMultipleCasesWithoutBreaks(): void {
    // Gosh, I have no idea what names of football shots are!
    $players = Vector {
      dict['name' => 'Ronaldo', 'favorite_shot' => 'freeKick'],
      dict['name' => 'Messi', 'favorite_shot' => 'slideKick'],
      dict['name' => 'Maradona', 'favorite_shot' => 'handOfGod'],
    };

    $body = $this
      ->getHackBuilder()
      ->startSwitch('$soccer_player')
      ->addCaseBlocks(
        $players,
        ($player, $body) ==> {
          $body
            ->addCase($player['name'], HackBuilderValues::export())
            ->addLinef('$shot = new Shot(\'%s\');', $player['favorite_shot'])
            ->unindent();
        },
      )
      ->addDefault()
      ->addLine('invariant_violation(\'ball deflated!\');')
      ->endDefault()
      ->endSwitch();
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testExportedVectorDoesNotHaveHHPrefix(): void {
    $body = $this
      ->getHackBuilder()
      ->add('$foo = ')
      ->addValue(
        Vector {1, 2, 3},
        HackBuilderValues::vector(HackBuilderValues::export()),
      )
      ->getCode();
    expect($body)->toContainSubstring('Vector');
    expect($body)->toNotContainSubstring('HH');
    expect_with_context(static::class, $body)->toBeUnchanged();
  }

  public function testVectorOfExportedVectors(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        Vector {Vector {'$foo', '$bar'}, Vector {'$herp', '$derp'}},
        HackBuilderValues::vector(
          HackBuilderValues::vector(HackBuilderValues::export()),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testVectorOfLiteralVectors(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        Vector {Vector {'$foo', '$bar'}, Vector {'$herp', '$derp'}},
        HackBuilderValues::vector(
          HackBuilderValues::vector(HackBuilderValues::literal()),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testVectorOfMaps(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        Vector {Map {'foo' => 'bar'}, Map {'herp' => 'derp'}},
        HackBuilderValues::vector(
          HackBuilderValues::map(
            HackBuilderKeys::export(),
            HackBuilderValues::export(),
          ),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testImmVectorOfImmVectors(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        ImmVector {ImmVector {'abc', 'def'}, ImmVector {'ghi', 'jkl'}},
        HackBuilderValues::immVector(
          HackBuilderValues::immVector(HackBuilderValues::export()),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testImmMapOfImmMaps(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        ImmMap {
          'foo' => ImmMap {'a' => 12, 'b' => 34},
          'bar' => ImmMap {'c' => 45},
        },
        HackBuilderValues::immMap(
          HackBuilderKeys::export(),
          HackBuilderValues::immMap(
            HackBuilderKeys::export(),
            HackBuilderValues::export(),
          ),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testImmSet(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        ImmSet {'abc', 'def'},
        HackBuilderValues::immSet(HackBuilderValues::export()),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testVec(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        vec['foo', 'bar'],
        HackBuilderValues::vec(HackBuilderValues::export()),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testKeyset(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        keyset['foo', 'bar'],
        HackBuilderValues::keyset(HackBuilderValues::export()),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testDict(): void {
    $body = $this
      ->getHackBuilder()
      ->addAssignment(
        '$foo',
        dict['foo' => 1, 'bar' => 2],
        HackBuilderValues::dict(
          HackBuilderKeys::export(),
          HackBuilderValues::export(),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testClassnameMap(): void {
    $body = $this
      ->getHackBuilder()
      ->addValue(
        Map {self::class => \stdClass::class},
        HackBuilderValues::map(
          HackBuilderKeys::classname(),
          HackBuilderValues::classname(),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }

  public function testLambdaMap(): void {
    $body = $this
      ->getHackBuilder()
      ->addValue(
        Map {'foo' => 'bar'},
        HackBuilderValues::map(
          HackBuilderKeys::lambda(($_config, $v) ==> "'key:".$v."'"),
          HackBuilderValues::lambda(($_config, $v) ==> "'value:".$v."'"),
        ),
      );
    expect_with_context(static::class, $body->getCode())->toBeUnchanged();
  }
}

final class TestAnotherCodegenConfig implements IHackCodegenConfig {
  public function getFileHeader(): ?Vector {
    return Vector {'Codegen Tests'};
  }

  public function getSpacesPerIndentation(): int {
    return 4;
  }

  public function getMaxLineLength(): int {
    return 40;
  }

  public function shouldUseTabs(): bool {
    return false;
  }

  public function getRootDir(): string {
    return '/';
  }

  public function getFormatter(): ?ICodegenFormatter {
    return null;
  }
}

Web Proxy Viewer  |  New URL  |  Original Page