diff --git a/Vida_Data/vevents.py b/Vida_Data/vevents.py index 60041da..0650edf 100644 --- a/Vida_Data/vevents.py +++ b/Vida_Data/vevents.py @@ -125,7 +125,9 @@ def readZoneSelection(theDict): if "selection" in theDict: theSelectionDict=theDict['selection'][0] #make sure the selection array has all three needed elements - if not ("attribute" and "logic" and "value") in theSelectionDict: + #(each one checked: ("attribute" and "logic" and "value") is just + #"value", so only that one used to be checked) + if not ("attribute" in theSelectionDict and "logic" in theSelectionDict and "value" in theSelectionDict): print("\n***WARNING: Selection array needs exactly three elements: attribute, logic, and value") print("\n***Ignoring selection criteria") zoneSelAttribute = "none" diff --git a/Vida_Data/vgraphics.py b/Vida_Data/vgraphics.py index 6e508d2..75d3ebb 100644 --- a/Vida_Data/vgraphics.py +++ b/Vida_Data/vgraphics.py @@ -187,7 +187,9 @@ def initCFDGText(theGarden, displayType, percentTimeStamp, maxHeightPlant): worldTypeRule=worldTypeRule+CFDGTopWorldRule worldTypeRule=worldTypeRule % (worldX+(worldX*0.3), worldY1+(worldY1*0.3), worldX, worldY1, worldX-1, worldY1-1) worldTypeRule=worldTypeRule+CFDGSideWorldRule - worldTypeRule=worldTypeRule % (worldX, worldY2, worldX-1, worldY2-1) + ###the side view's rule takes six numbers, as in the other side views + ###(it used to be given four, which crashed) + worldTypeRule=worldTypeRule % (worldX+(worldX*0.3), worldY2+(worldY1*0.3), worldX, worldY2, worldX-1, worldY2-1) if percentTimeStamp>0.0: timeStampCode= CFDGTimeStamp diff --git a/Vida_Data/vplacement.py b/Vida_Data/vplacement.py index b3fd768..8633b88 100644 --- a/Vida_Data/vplacement.py +++ b/Vida_Data/vplacement.py @@ -47,15 +47,19 @@ def checkSeedPlacementList(seedPlacementList): aLine = [correctType(x) for x in aLine] seedPlacementList[i]=aLine i=i+1 - i=0 + ###keep the lines that are a species name, x, y and a whole number of + ###cycles. (This used to delete the bad lines from the list while going + ###through it, which skipped the line after each one it deleted.) + goodLines=[] printErrorMessage=0 for aLine in seedPlacementList: if type(aLine[3])==float: aLine[3]=int(aLine[3]) if (type(aLine[0])!=str) or (type(aLine[1])!=float) or (type(aLine[2])!=float) or (type(aLine[3])!=int): printErrorMessage=1 - del seedPlacementList[i] - i=i+1 + else: + goodLines.append(aLine) + seedPlacementList=goodLines if printErrorMessage: print("***Improper seeding file format...") print(" Questionable lines will be ignored.") diff --git a/tests/characterization/README.md b/tests/characterization/README.md index f58e887..22314a2 100644 --- a/tests/characterization/README.md +++ b/tests/characterization/README.md @@ -180,10 +180,11 @@ been fixed since, and each now has a scenario that uses the feature. a `TypeError` on Python 3.12+, and on 3.11 it was already a `ValueError` for worlds of odd size. -Still open: the combined bottom + top + side view (`-g bts`) crashes with -`TypeError: not enough arguments for format string`, because -`initCFDGText` in `vgraphics.py` fills the side-view template, which needs -six numbers, with only four. The `graphics_outputs` scenario pins this. +5. Fixed: the combined bottom + top + side view (`-g bts`) crashed with + `TypeError: not enough arguments for format string`, because + `initCFDGText` in `vgraphics.py` filled the side-view template, which + needs six numbers, with only four. The `graphics_outputs` scenario now + makes that view too. Also noticed, but not crashes: in `disperseSeed`, the terrain binary search calls `elevationFromPixel(thePixelValue)` without `theGarden.maxElevation`, diff --git a/tests/characterization/golden/graphics_outputs.json.xz b/tests/characterization/golden/graphics_outputs.json.xz index abc9266..2fd17d6 100644 Binary files a/tests/characterization/golden/graphics_outputs.json.xz and b/tests/characterization/golden/graphics_outputs.json.xz differ diff --git a/tests/characterization/scenarios/graphics_outputs.yml b/tests/characterization/scenarios/graphics_outputs.yml index 2bb975e..02b0d20 100644 --- a/tests/characterization/scenarios/graphics_outputs.yml +++ b/tests/characterization/scenarios/graphics_outputs.yml @@ -2,9 +2,10 @@ description: > Graphics output: the ContextFree (.cfdg) files and the 3D .dxf files, kept on disk (-c) so their names and contents are pinned. Stage 1 makes the combined bottom-up + side view and 3D; stage 2 makes every other view - except bottom + top + side (-g bts), which currently crashes (stage 3): - initCFDGText fills the side view template, which needs six numbers, with - only four. The external cfdg and ffmpeg programs are not run. + except bottom + top + side (-g bts), which is stage 3. (That view used to + crash: initCFDGText filled the side view template, which needs six + numbers, with only four.) The external cfdg and ffmpeg programs are not + run. species: [species/generic] stages: - rngstart: 808 diff --git a/tests/unit/test_events.py b/tests/unit/test_events.py index 7913520..3529039 100644 --- a/tests/unit/test_events.py +++ b/tests/unit/test_events.py @@ -44,6 +44,15 @@ def test_a_zone_without_a_selection_selects_everything(): assert vevents.readZoneSelection({})[0] == "none" +def test_a_selection_missing_a_part_is_ignored(): + # Each of attribute, logic and value is needed. Only value used to be + # checked, so a selection without attribute or logic crashed. + for missing in ["attribute", "logic", "value"]: + selection = {"attribute": "heightStem", "logic": ">", "value": 2.0} + del selection[missing] + assert vevents.readZoneSelection({"selection": [selection]})[0] == "none" + + def test_a_selection_with_bad_logic_is_ignored(): zone = {"selection": [{"attribute": "heightStem", "logic": "!=", "value": 2.0}]} assert vevents.readZoneSelection(zone)[0] == "none" diff --git a/tests/unit/test_placement.py b/tests/unit/test_placement.py index 018244e..38d4bd1 100644 --- a/tests/unit/test_placement.py +++ b/tests/unit/test_placement.py @@ -23,8 +23,9 @@ def test_whole_number_coordinates_are_rejected(): assert lines == [["random", 1.0, 2.0, 0]] -def test_after_a_bad_line_the_next_line_is_not_checked(): - # The check deletes bad lines from the list while looping over it, so the - # line after a deleted one is skipped: here the second bad line is kept. - lines = vplacement.checkSeedPlacementList(["a.yml, 1, 2.0, 0", "b.yml, 3, 4.0, 0", "c.yml, 5.0, 6.0, 0"]) - assert lines == [["b.yml", 3, 4.0, 0], ["c.yml", 5.0, 6.0, 0]] +def test_every_bad_line_is_left_out_and_every_good_line_kept(): + # This used to delete bad lines from the list while looping over it, which + # skipped the line after each deleted one: the second bad line was kept. + lines = vplacement.checkSeedPlacementList( + ["a.yml, 1, 2.0, 0", "b.yml, 3, 4.0, 0", "c.yml, 5.0, 6.0, 0", "d.yml, 7, 8.0, 0", "e.yml, 9.0, 1.0, 2"]) + assert lines == [["c.yml", 5.0, 6.0, 0], ["e.yml", 9.0, 1.0, 2]]