diff --git a/.github/workflows/haskell-release.yml b/.github/workflows/haskell-release.yml index c21de00f8..f7c84932e 100644 --- a/.github/workflows/haskell-release.yml +++ b/.github/workflows/haskell-release.yml @@ -10,178 +10,142 @@ on: - "[0-9]+.[0-9]+.[0-9]+.[0-9]+*" jobs: - create_release: - name: Create Github Release - runs-on: ubuntu-latest + build-binaries: + name: Haskell-Release - ${{ matrix.os }} - ${{ matrix.ghc }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + # macos-13 points to x64 based OS and macos-latest points to arm based OS + os: + - ubuntu-latest + - macos-latest + - macos-13 + cabal: + - 3.12.1.0 + ghc: + - 9.8.2 + steps: - name: Check out code uses: actions/checkout@v4 - - name: Zip data directory - run: | - zip -r swarm-data.zip ./data || { echo "Unable to create a zip archive."; exit 1; } - - - name: Create Release - id: create_release - uses: softprops/action-gh-release@v1 - with: - files: | - LICENSE - swarm-data.zip - - - name: Output Release URL File - run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt - - - name: Save Release URL File for publish - uses: actions/upload-artifact@v4 - with: - name: release_url - path: release_url.txt - - build_artifact: - needs: [create_release] - name: Haskell-Release - ${{ matrix.os }} - ${{ matrix.compiler }} - runs-on: ubuntu-20.04 - container: - # It might be good idea to use older runner for building binary: - # on latest runners like fedora, it would link symbols in newer - # system libraries, so users could not run it on older OSes - image: buildpack-deps:jammy - strategy: - matrix: - include: - - compiler: ghc-9.8.2 - compilerKind: ghc - compilerVersion: 9.8.2 - setup-method: ghcup - steps: - - name: apt - run: | - apt-get update - apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5 - mkdir -p "$HOME/.ghcup/bin" - curl -sL https://downloads.haskell.org/ghcup/0.1.30.0/x86_64-linux-ghcup-0.1.30.0 > "$HOME/.ghcup/bin/ghcup" - chmod a+x "$HOME/.ghcup/bin/ghcup" - "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false) - "$HOME/.ghcup/bin/ghcup" install cabal 3.12.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false) - env: - HCKIND: ${{ matrix.compilerKind }} - HCNAME: ${{ matrix.compiler }} - HCVER: ${{ matrix.compilerVersion }} - - name: Set PATH and environment variables - run: | - echo "$HOME/.cabal/bin" >> $GITHUB_PATH - echo "LANG=C.UTF-8" >> "$GITHUB_ENV" - echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV" - echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV" - HCDIR=/opt/$HCKIND/$HCVER - HC=$HOME/.ghcup/bin/$HCKIND-$HCVER - echo "HC=$HC" >> "$GITHUB_ENV" - echo "HCPKG=$HOME/.ghcup/bin/$HCKIND-pkg-$HCVER" >> "$GITHUB_ENV" - echo "HADDOCK=$HOME/.ghcup/bin/haddock-$HCVER" >> "$GITHUB_ENV" - echo "CABAL=$HOME/.ghcup/bin/cabal-3.12.1.0 -vnormal+nowrap" >> "$GITHUB_ENV" - HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))') - echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV" - echo "HEADHACKAGE=false" >> "$GITHUB_ENV" - echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV" - env: - HCKIND: ${{ matrix.compilerKind }} - HCNAME: ${{ matrix.compiler }} - HCVER: ${{ matrix.compilerVersion }} - - name: env - run: | - env - - name: write cabal config - run: | - mkdir -p $CABAL_DIR - cat >> $CABAL_CONFIG <> $CABAL_CONFIG <> "$GITHUB_ENV" - - name: Load Release URL File from release job - uses: actions/download-artifact@v4 - with: - name: release_url + - if: matrix.os == 'ubuntu-latest' + name: Set binary OS name on Ubuntu + run: echo BINARY_OS=${{ runner.os }} >> "$GITHUB_ENV" - - name: Get Release File Name & Upload URL - id: get_release_info - run: | - echo "upload_url=$(cat release_url.txt)" >> "$GITHUB_OUTPUT" + - if: (matrix.os == 'macos-13') || (matrix.os == 'macos-latest') + name: Set binary OS name on Macos + run: echo BINARY_OS="Darwin" >> "$GITHUB_ENV" - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.get_release_info.outputs.upload_url }} - asset_path: ${{ env.BINARY_PATH }} - asset_name: swarm-${{ runner.os }} - asset_content_type: application/octet-stream - - - name: Generate tarball for Hackage - run: $CABAL v2-sdist + - name: Set binary Arch name + run: echo ARCH="x86_64" >> "$GITHUB_ENV" - # - name: Generate documentation for Hackage - # # The hackage-server attempts to build documentation for library packages, but this can fail. - # # If it does we can do it ourselves - # run: cabal v2-haddock --builddir=docs --haddock-for-hackage --enable-doc + - if: matrix.os == 'macos-latest' + name: Set binary ARCH name for apple silicon + run: echo ARCH="arm64" >> "$GITHUB_ENV" - - uses: haskell-actions/hackage-publish@v1 + - name: Upload executables + uses: actions/upload-artifact@v4 with: - hackageToken: "${{ secrets.HACKAGE_AUTH_TOKEN }}" - packagesPath: dist-newstyle/sdist - # docsPath: docs - publish: true + name: swarm-${{ env.BINARY_OS }}-${{env.ARCH}} + path: ${{ env.BINARY_PATH }} + retention-days: 3 + + create-release: + name: Create release + needs: [build-binaries] + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Download Linux x86_64 + uses: actions/download-artifact@v4 + with: + name: swarm-Linux-x86_64 + path: artifacts/swarm-Linux-x86_64 + + - name: Download executable for Macos x86_64 + uses: actions/download-artifact@v4 + with: + name: swarm-Darwin-x86_64 + path: artifacts/swarm-Darwin-x86_64 + + - name: Download executable for arm64 Macos + uses: actions/download-artifact@v4 + with: + name: swarm-Darwin-arm64 + path: artifacts/swarm-Darwin-arm64 + + - name: Download Swarm data + uses: actions/download-artifact@v4 + with: + name: swarm-data.zip + path: artifacts/swarm-Darwin-arm64 + + - name: Rename executables + run: | + mv artifacts/swarm-Linux-x86_64/swarm swarm-Linux-x86_64 + mv artifacts/swarm-Darwin-x86_64/swarm swarm-Darwin-x86_64 + mv artifacts/swarm-Darwin-arm64/swarm swarm-Darwin-arm64 + + - name: Zip data directory + run: zip -r swarm-data.zip ./data || { echo "Unable to create a zip archive."; exit 1; } + + - name: Upload Release Asset + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: swarm + draft: false + prerelease: false + files: | + swarm-Linux-x86_64 + swarm-Darwin-x86_64 + swarm-Darwin-arm64 + LICENSE + swarm-data.zip + + # - name: Generate documentation for Hackage + # # The hackage-server attempts to build documentation for library packages, but this can fail. + # # If it does we can do it ourselves + # run: cabal v2-haddock --builddir=docs --haddock-for-hackage --enable-doc + + - uses: haskell-actions/hackage-publish@v1 + with: + hackageToken: "${{ secrets.HACKAGE_AUTH_TOKEN }}" + packagesPath: dist-newstyle/sdist + # docsPath: docs + publish: true diff --git a/data/scenarios/Testing/1780-structure-merge-expansion/00-ORDER.txt b/data/scenarios/Testing/1780-structure-merge-expansion/00-ORDER.txt index 8580fa46e..ed9116ef4 100644 --- a/data/scenarios/Testing/1780-structure-merge-expansion/00-ORDER.txt +++ b/data/scenarios/Testing/1780-structure-merge-expansion/00-ORDER.txt @@ -1,3 +1,4 @@ nonoverlapping-structure-merge.yaml root-map-expansion.yaml -structure-composition.yaml \ No newline at end of file +structure-composition.yaml +sequential-placement.yaml \ No newline at end of file diff --git a/data/scenarios/Testing/1780-structure-merge-expansion/sequential-placement.yaml b/data/scenarios/Testing/1780-structure-merge-expansion/sequential-placement.yaml new file mode 100644 index 000000000..c007f7c9c --- /dev/null +++ b/data/scenarios/Testing/1780-structure-merge-expansion/sequential-placement.yaml @@ -0,0 +1,95 @@ +version: 1 +name: Flipped structure placement +author: Karl Ostmo +description: | + Sequentially place structures that are larger than the map + with flipped orientation. +robots: + - name: base + dir: north +creative: true +objectives: + - goal: + - Must have 3 of each color visible + condition: | + def countColor = \e. + resonate e ((0, 0), (10, -5)); + end; + + as base { + r <- countColor "pixel (R)"; + g <- countColor "pixel (G)"; + b <- countColor "pixel (B)"; + y <- countColor "gold"; + return $ r == 3 && g == 3 && b == 3 && y == 3; + } +solution: | + noop +known: [boulder, log, pixel (R), pixel (G), pixel (B), gold] +world: + structures: + - name: reddish + structure: + mask: '.' + palette: + 'x': [stone, pixel (R)] + map: | + xx + x. + - name: greenish + structure: + mask: '.' + palette: + 'x': [stone, pixel (G)] + map: | + xx + x. + - name: bluish + structure: + mask: '.' + palette: + 'x': [stone, pixel (B)] + map: | + xx + x. + - name: goldish + structure: + mask: '.' + palette: + 'x': [stone, gold] + map: | + xx + x. + - name: block + structure: + mask: '.' + palette: + 'x': [ice, log] + placements: + - src: greenish + orient: + flip: true + offset: [-3, 2] + - src: reddish + offset: [-6, 0] + - src: goldish + orient: + flip: true + offset: [3, -1] + - src: bluish + offset: [0, 1] + map: | + xxx + xx. + x.. + palette: + 'Ω': [grass, erase, base] + mask: '.' + placements: + - src: block + offset: [0, -1] + upperleft: [0, 0] + dsl: | + {grass} + map: | + Ω diff --git a/data/scenarios/Testing/562-lodestone.sw b/data/scenarios/Testing/562-lodestone.sw index 07c49964b..7ca7cdda3 100644 --- a/data/scenarios/Testing/562-lodestone.sw +++ b/data/scenarios/Testing/562-lodestone.sw @@ -5,8 +5,10 @@ def ifC = \p.\t.\e. if b t e end; -def until = \p.\t. - ifC p t {until p t} +def waitFor = \e.\t. + watch down; + wait 100; + ifC (ishere e) t {waitFor e t} end; def repeat = \c. @@ -29,18 +31,18 @@ end; // get one lodestone build {log "Hey!"; turn north; m2; l <- grab; turn back; m2; place l}; -until (ishere "lodestone") {grab}; +waitFor "lodestone" {grab}; // get two bit (0) build { log "Hi!"; repeat ( log "I am going for a bit"; - turn east; m2; x <- until (ishere "bit (0)") {harvest}; turn back; m2; place x; + turn east; m2; x <- waitFor "bit (0)" {harvest}; turn back; m2; place x; log "I brought a bit"; )}; -until (ishere "bit (0)") {grab}; -until (ishere "bit (0)") {grab}; +waitFor "bit (0)" {grab}; +waitFor "bit (0)" {grab}; make "bit (1)"; make "drill bit" diff --git a/data/scenarios/Testing/562-lodestone.yaml b/data/scenarios/Testing/562-lodestone.yaml index f97f071f7..bbdb6bf5e 100644 --- a/data/scenarios/Testing/562-lodestone.yaml +++ b/data/scenarios/Testing/562-lodestone.yaml @@ -25,6 +25,7 @@ robots: - workbench - 3D printer - branch predictor + - rolex inventory: - [10, drill] - [10, lambda] @@ -37,6 +38,7 @@ robots: - [10, strange loop] - [10, branch predictor] - [10, toolkit] + - [10, rolex] - [0, drill bit] - [0, bit (0)] - [0, bit (1)] diff --git a/data/test/standalone-topography/circle-and-crosses.yaml b/data/test/standalone-topography/circle-and-crosses.yaml index e5f650bd1..dada1adfd 100644 --- a/data/test/standalone-topography/circle-and-crosses.yaml +++ b/data/test/standalone-topography/circle-and-crosses.yaml @@ -19,7 +19,7 @@ structures: fff placements: - src: beam - offset: [0, 3] + offset: [0, 0] - src: beam offset: [-3, -3] orient: diff --git a/scripts/validate/issues-for-todos.sh b/scripts/validate/issues-for-todos.sh index 55e9153e1..41de14ac1 100755 --- a/scripts/validate/issues-for-todos.sh +++ b/scripts/validate/issues-for-todos.sh @@ -3,7 +3,7 @@ cd $(git rev-parse --show-toplevel) -if grep --line-number --include \*.hs -riP '(TODO|FIXME|XXX)\b' src app 2>&1 | grep -vP '#\d+'; then +if grep --line-number --include \*.hs -riP '(TODO|FIXME|XXX)\b' src app test 2>&1 | grep -vP '#\d+'; then echo "Please add a link to Issue, for example: TODO: #123" exit 1 else diff --git a/src/swarm-scenario/Swarm/Game/Scenario/Topography/Navigation/Portal.hs b/src/swarm-scenario/Swarm/Game/Scenario/Topography/Navigation/Portal.hs index 96e6ed114..5db64fc72 100644 --- a/src/swarm-scenario/Swarm/Game/Scenario/Topography/Navigation/Portal.hs +++ b/src/swarm-scenario/Swarm/Game/Scenario/Topography/Navigation/Portal.hs @@ -179,7 +179,7 @@ validatePartialNavigation currentSubworldName upperLeft unmergedWaypoints portal correctedWaypoints = binTuples $ map - (\x -> (wpName $ wpConfig $ value x, fmap (offsetLoc $ upperLeft .-. origin) x)) + (\x -> (wpName $ wpConfig $ value x, fmap (offsetLoc $ asVector upperLeft) x)) unmergedWaypoints bareWaypoints = M.map (NE.map extractLoc) correctedWaypoints waypointsWithUniqueFlag = M.filter (any $ wpUnique . wpConfig . value) correctedWaypoints diff --git a/src/swarm-topography/Swarm/Game/Location.hs b/src/swarm-topography/Swarm/Game/Location.hs index f1a80ee01..884705733 100644 --- a/src/swarm-topography/Swarm/Game/Location.hs +++ b/src/swarm-topography/Swarm/Game/Location.hs @@ -29,6 +29,7 @@ module Swarm.Game.Location ( -- ** Utility functions manhattan, euclidean, + asVector, getLocsInArea, getElemsInArea, @@ -199,6 +200,10 @@ manhattan (Location x1 y1) (Location x2 y2) = abs (x1 - x2) + abs (y1 - y2) euclidean :: Location -> Location -> Double euclidean p1 p2 = norm (fromIntegral <$> (p2 .-. p1)) +-- | Converts a 'Point' to a vector offset from the 'origin'. +asVector :: Location -> V2 Int32 +asVector loc = loc .-. origin + -- | Get all the locations that are within a certain manhattan -- distance from a given location. -- diff --git a/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Assembly.hs b/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Assembly.hs index 06a6e12ac..3c620ef99 100644 --- a/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Assembly.hs +++ b/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Assembly.hs @@ -22,7 +22,6 @@ import Data.Text qualified as T import Linear.Affine import Swarm.Game.Location import Swarm.Game.Scenario.Topography.Area -import Swarm.Game.Scenario.Topography.Grid import Swarm.Game.Scenario.Topography.Navigation.Waypoint import Swarm.Game.Scenario.Topography.Placement import Swarm.Game.Scenario.Topography.Structure @@ -42,14 +41,14 @@ overlaySingleStructure :: Either Text (MergedStructure (Maybe a)) overlaySingleStructure inheritedStrucDefs - (Placed p@(Placement _ pose@(Pose loc orientation)) ns) + (Placed p@(Placement _sName pose@(Pose loc orientation)) ns) (MergedStructure inputArea inputPlacements inputWaypoints) = do MergedStructure overlayArea overlayPlacements overlayWaypoints <- mergeStructures inheritedStrucDefs (WithParent p) $ structure ns let mergedWaypoints = inputWaypoints <> map (fmap $ placeOnArea overlayArea) overlayWaypoints mergedPlacements = inputPlacements <> map (placeOnArea overlayArea) overlayPlacements - mergedArea = overlayGridExpanded (gridContent inputArea) pose overlayArea + mergedArea = overlayGridExpanded inputArea pose overlayArea return $ MergedStructure mergedArea mergedPlacements mergedWaypoints where @@ -81,6 +80,8 @@ mergeStructures inheritedStrucDefs parentPlacement (Structure origArea subStruct map wrapPlacement $ filter (\(Placed _ ns) -> isRecognizable ns) overlays + -- NOTE: Each successive overlay may alter the coordinate origin. + -- We make sure this new origin is propagated to subsequent sibling placements. foldlM (flip $ overlaySingleStructure structureMap) (MergedStructure origArea wrappedOverlays originatedWaypoints) @@ -97,18 +98,22 @@ mergeStructures inheritedStrucDefs parentPlacement (Structure origArea subStruct -- * Grid manipulation overlayGridExpanded :: - Grid (Maybe a) -> + PositionedGrid (Maybe a) -> Pose -> PositionedGrid (Maybe a) -> PositionedGrid (Maybe a) overlayGridExpanded - inputGrid - (Pose loc orientation) - (PositionedGrid _ overlayArea) = - PositionedGrid origin inputGrid <> positionedOverlay + baseGrid + (Pose yamlPlacementOffset orientation) + -- NOTE: The '_childAdjustedOrigin' is the sum of origin adjustments + -- to completely assemble some substructure. However, we discard + -- this when we place a substructure into a new base grid. + (PositionedGrid _childAdjustedOrigin overlayArea) = + baseGrid <> positionedOverlay where reorientedOverlayCells = applyOrientationTransform orientation overlayArea - positionedOverlay = PositionedGrid loc reorientedOverlayCells + placementAdjustedByOrigin = gridPosition baseGrid .+^ asVector yamlPlacementOffset + positionedOverlay = PositionedGrid placementAdjustedByOrigin reorientedOverlayCells -- * Validation diff --git a/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Overlay.hs b/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Overlay.hs index 86444ed5a..cb95e82a1 100644 --- a/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Overlay.hs +++ b/src/swarm-topography/Swarm/Game/Scenario/Topography/Structure/Overlay.hs @@ -101,12 +101,11 @@ instance (Alternative f) => Semigroup (PositionedGrid (f a)) where mergedSize = computeMergedArea $ OverlayPair a1 a2 combinedGrid = zipGridRows mergedSize paddedOverlayPair - -- We subtract the base origin from the - -- overlay position, such that the displacement vector - -- will have: + -- We create a vector from the overlay position, + -- such that the displacement vector will have: -- \* negative X component if the origin must be shifted east -- \* positive Y component if the origin must be shifted south - originDelta@(V2 deltaX deltaY) = overlayLoc .-. baseLoc + originDelta@(V2 deltaX deltaY) = asVector overlayLoc -- Note that the adjustment vector will only ever have -- a non-negative X component (i.e. loc of upper-left corner must be shifted east) and -- a non-positive Y component (i.e. loc of upper-left corner must be shifted south). diff --git a/src/swarm-tui/Swarm/TUI/View.hs b/src/swarm-tui/Swarm/TUI/View.hs index 4a9cae96a..e552c6d87 100644 --- a/src/swarm-tui/Swarm/TUI/View.hs +++ b/src/swarm-tui/Swarm/TUI/View.hs @@ -61,13 +61,13 @@ import Data.Map qualified as M import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, maybeToList) import Data.Semigroup (sconcat) import Data.Sequence qualified as Seq -import Data.Set qualified as Set (toList) +import Data.Set qualified as Set import Data.Text (Text) import Data.Text qualified as T import Data.Time (NominalDiffTime, defaultTimeLocale, formatTime) import Network.Wai.Handler.Warp (Port) import Swarm.Constant -import Swarm.Game.Device (commandCost, commandsForDeviceCaps, enabledCommands, getMap, ingredients) +import Swarm.Game.Device (commandCost, commandsForDeviceCaps, enabledCommands, getCapabilitySet, getMap, ingredients) import Swarm.Game.Display import Swarm.Game.Entity as E import Swarm.Game.Ingredients @@ -539,14 +539,12 @@ drawClockDisplay lgTPS gs = hBox . intersperse (txt " ") $ catMaybes [clockWidge clockWidget = maybeDrawTime (gs ^. temporal . ticks) (gs ^. temporal . paused || lgTPS < 3) gs pauseWidget = guard (gs ^. temporal . paused) $> txt "(PAUSED)" --- | Check whether the currently focused robot (if any) has a clock --- device equipped. +-- | Check whether the currently focused robot (if any) has some kind +-- of a clock device equipped. clockEquipped :: GameState -> Bool clockEquipped gs = case focusedRobot gs of Nothing -> False - Just r - | countByName "clock" (r ^. equippedDevices) > 0 -> True - | otherwise -> False + Just r -> CExecute Time `Set.member` getCapabilitySet (r ^. robotCapabilities) -- | Format a ticks count as a hexadecimal clock. drawTime :: TickNumber -> Bool -> String diff --git a/test/integration/Main.hs b/test/integration/Main.hs index 9f0fa9c8c..a2c72b1ea 100644 --- a/test/integration/Main.hs +++ b/test/integration/Main.hs @@ -440,6 +440,14 @@ testScenarioSolutions rs ui key = [ testSolution Default "Testing/1535-ping/1535-in-range" , testSolution Default "Testing/1535-ping/1535-out-of-range" ] + , testGroup + "Structure placement (#1780)" + [ testSolution Default "Testing/1780-structure-merge-expansion/sequential-placement" + -- TODO(#2148) define goal conditions or convert to image fixtures + -- , testSolution Default "Testing/1780-structure-merge-expansion/nonoverlapping-structure-merge" + -- , testSolution Default "Testing/1780-structure-merge-expansion/root-map-expansion" + -- , testSolution Default "Testing/1780-structure-merge-expansion/structure-composition" + ] , testGroup "Structure recognition (#1575)" [ testSolution Default "Testing/1575-structure-recognizer/1575-browse-structures" diff --git a/test/standalone-topography/src/Lib.hs b/test/standalone-topography/src/Lib.hs index 1fa47d2c5..c73991d8e 100644 --- a/test/standalone-topography/src/Lib.hs +++ b/test/standalone-topography/src/Lib.hs @@ -32,8 +32,12 @@ parseStructures dataDir baseFilename = do dataDir "test/standalone-topography" baseFilename return $ forceEither $ left prettyPrintParseException eitherResult -compareToReferenceImage :: FilePath -> Assertion -compareToReferenceImage fileStem = do +compareToReferenceImage :: + -- | set this to update the golden tests + Bool -> + FilePath -> + Assertion +compareToReferenceImage refreshReferenceImage fileStem = do dataDir <- getDataDir parentStruct <- parseStructures dataDir $ fileStem <.> "yaml" let MergedStructure overlayArea _ _ = forceEither $ mergeStructures mempty Root parentStruct @@ -44,6 +48,3 @@ compareToReferenceImage fileStem = do else do decodedImg <- LBS.readFile referenceFilepath assertEqual "Generated image must equal reference image!" decodedImg encodedImgBytestring - where - -- Manually toggle this to update the golden tests - refreshReferenceImage = False diff --git a/test/standalone-topography/src/Main.hs b/test/standalone-topography/src/Main.hs index cd8438307..399d21a04 100644 --- a/test/standalone-topography/src/Main.hs +++ b/test/standalone-topography/src/Main.hs @@ -4,33 +4,46 @@ -- SPDX-License-Identifier: BSD-3-Clause module Main where -import Test.Tasty (defaultMain, testGroup) +import Data.Proxy +import Data.Typeable (Typeable) +import Lib (compareToReferenceImage) +import Test.Tasty import Test.Tasty.HUnit (testCase) +import Test.Tasty.Options -import Lib +newtype UpdateGoldenTests = UpdateGoldenTests Bool + deriving (Eq, Ord, Typeable) + +instance IsOption UpdateGoldenTests where + parseValue = fmap UpdateGoldenTests . safeRead + defaultValue = UpdateGoldenTests False + optionName = return "refresh" + optionHelp = return "Should overwrite the golden test images" + optionCLParser = mkFlagCLParser mempty (UpdateGoldenTests True) main :: IO () main = do - defaultMain $ - testGroup - "Test structure assembly" - [ mkGroup - "Black and white" - [ "circle-and-crosses" - , "checkerboard" - ] - , mkGroup - "Color" - [ "rainbow" + defaultMainWithIngredients ingredients $ askOption $ \(UpdateGoldenTests shouldRefreshTests) -> + let doTest stem = + testCase (unwords ["Image equality:", stem]) $ + compareToReferenceImage shouldRefreshTests stem + + mkGroup title members = + testGroup title $ + map + doTest + members + in testGroup + "Test structure assembly" + [ mkGroup + "Black and white" + [ "circle-and-crosses" + , "checkerboard" + ] + , mkGroup + "Color" + [ "rainbow" + ] ] - ] where - doTest stem = - testCase (unwords ["Image equality:", stem]) $ - compareToReferenceImage stem - - mkGroup title members = - testGroup title $ - map - doTest - members + ingredients = includingOptions [Option (Proxy :: Proxy UpdateGoldenTests)] : defaultIngredients diff --git a/web/play.html b/web/play.html index 03da4b805..cbbdd4397 100644 --- a/web/play.html +++ b/web/play.html @@ -2,20 +2,20 @@ Web frontend - + -