floodmodeller-api 0.4.2.post1__py3-none-any.whl → 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. floodmodeller_api/__init__.py +8 -9
  2. floodmodeller_api/_base.py +169 -176
  3. floodmodeller_api/backup.py +273 -273
  4. floodmodeller_api/dat.py +889 -831
  5. floodmodeller_api/diff.py +136 -119
  6. floodmodeller_api/ied.py +302 -306
  7. floodmodeller_api/ief.py +553 -637
  8. floodmodeller_api/ief_flags.py +253 -253
  9. floodmodeller_api/inp.py +260 -266
  10. floodmodeller_api/libs/libifcoremd.dll +0 -0
  11. floodmodeller_api/libs/libifcoremt.so.5 +0 -0
  12. floodmodeller_api/libs/libifport.so.5 +0 -0
  13. floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
  14. floodmodeller_api/libs/libintlc.so.5 +0 -0
  15. floodmodeller_api/libs/libmmd.dll +0 -0
  16. floodmodeller_api/libs/libsvml.so +0 -0
  17. floodmodeller_api/libs/libzzn_read.so +0 -0
  18. floodmodeller_api/libs/zzn_read.dll +0 -0
  19. floodmodeller_api/logs/__init__.py +2 -2
  20. floodmodeller_api/logs/lf.py +364 -312
  21. floodmodeller_api/logs/lf_helpers.py +354 -352
  22. floodmodeller_api/logs/lf_params.py +643 -529
  23. floodmodeller_api/mapping.py +84 -0
  24. floodmodeller_api/test/__init__.py +4 -4
  25. floodmodeller_api/test/conftest.py +16 -8
  26. floodmodeller_api/test/test_backup.py +117 -117
  27. floodmodeller_api/test/test_conveyance.py +107 -0
  28. floodmodeller_api/test/test_dat.py +222 -92
  29. floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
  30. floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
  31. floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
  32. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
  33. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
  34. floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
  35. floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
  36. floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
  37. floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
  38. floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
  39. floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
  40. floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
  41. floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
  42. floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
  43. floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
  44. floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
  45. floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
  46. floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
  47. floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
  48. floodmodeller_api/test/test_data/EX1.DAT +321 -321
  49. floodmodeller_api/test/test_data/EX1.ext +107 -107
  50. floodmodeller_api/test/test_data/EX1.feb +320 -320
  51. floodmodeller_api/test/test_data/EX1.gxy +107 -107
  52. floodmodeller_api/test/test_data/EX17.DAT +421 -422
  53. floodmodeller_api/test/test_data/EX17.ext +213 -213
  54. floodmodeller_api/test/test_data/EX17.feb +422 -422
  55. floodmodeller_api/test/test_data/EX18.DAT +375 -375
  56. floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
  57. floodmodeller_api/test/test_data/EX2.DAT +302 -302
  58. floodmodeller_api/test/test_data/EX3.DAT +926 -926
  59. floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
  60. floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
  61. floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
  62. floodmodeller_api/test/test_data/EX6.ext +532 -532
  63. floodmodeller_api/test/test_data/EX6.feb +2084 -2084
  64. floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
  65. floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
  66. floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
  67. floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
  68. floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
  69. floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
  70. floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
  71. floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
  72. floodmodeller_api/test/test_data/blockage.dat +50 -50
  73. floodmodeller_api/test/test_data/blockage.ext +45 -45
  74. floodmodeller_api/test/test_data/blockage.feb +9 -9
  75. floodmodeller_api/test/test_data/blockage.gxy +71 -71
  76. floodmodeller_api/test/test_data/conveyance_test.dat +165 -0
  77. floodmodeller_api/test/test_data/conveyance_test.feb +116 -0
  78. floodmodeller_api/test/test_data/conveyance_test.gxy +85 -0
  79. floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
  80. floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
  81. floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
  82. floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
  83. floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
  84. floodmodeller_api/test/test_data/ex3.ief +20 -20
  85. floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
  86. floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
  87. floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
  88. floodmodeller_api/test/test_data/example1.inp +329 -329
  89. floodmodeller_api/test/test_data/example2.inp +158 -158
  90. floodmodeller_api/test/test_data/example3.inp +297 -297
  91. floodmodeller_api/test/test_data/example4.inp +388 -388
  92. floodmodeller_api/test/test_data/example5.inp +147 -147
  93. floodmodeller_api/test/test_data/example6.inp +154 -154
  94. floodmodeller_api/test/test_data/expected_conveyance.csv +60 -0
  95. floodmodeller_api/test/test_data/jump.dat +176 -176
  96. floodmodeller_api/test/test_data/network.dat +1374 -1374
  97. floodmodeller_api/test/test_data/network.ext +45 -45
  98. floodmodeller_api/test/test_data/network.exy +1 -1
  99. floodmodeller_api/test/test_data/network.feb +45 -45
  100. floodmodeller_api/test/test_data/network.ied +45 -45
  101. floodmodeller_api/test/test_data/network.ief +20 -20
  102. floodmodeller_api/test/test_data/network.inp +147 -147
  103. floodmodeller_api/test/test_data/network.pxy +57 -57
  104. floodmodeller_api/test/test_data/network.zzd +122 -122
  105. floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
  106. floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
  107. floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
  108. floodmodeller_api/test/test_data/rnweir.dat +9 -9
  109. floodmodeller_api/test/test_data/rnweir.ext +45 -45
  110. floodmodeller_api/test/test_data/rnweir.feb +9 -9
  111. floodmodeller_api/test/test_data/rnweir.gxy +45 -45
  112. floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
  113. floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
  114. floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
  115. floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
  116. floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
  117. floodmodeller_api/test/test_data/unit checks.dat +16 -16
  118. floodmodeller_api/test/test_ied.py +29 -29
  119. floodmodeller_api/test/test_ief.py +136 -24
  120. floodmodeller_api/test/test_inp.py +47 -48
  121. floodmodeller_api/test/test_json.py +114 -0
  122. floodmodeller_api/test/test_logs_lf.py +102 -51
  123. floodmodeller_api/test/test_tool.py +165 -152
  124. floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
  125. floodmodeller_api/test/test_xml2d.py +151 -156
  126. floodmodeller_api/test/test_zzn.py +36 -34
  127. floodmodeller_api/to_from_json.py +230 -0
  128. floodmodeller_api/tool.py +332 -329
  129. floodmodeller_api/toolbox/__init__.py +5 -5
  130. floodmodeller_api/toolbox/example_tool.py +45 -45
  131. floodmodeller_api/toolbox/model_build/__init__.py +2 -2
  132. floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -98
  133. floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
  134. floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
  135. floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -76
  136. floodmodeller_api/units/__init__.py +10 -10
  137. floodmodeller_api/units/_base.py +214 -212
  138. floodmodeller_api/units/boundaries.py +467 -467
  139. floodmodeller_api/units/comment.py +52 -55
  140. floodmodeller_api/units/conduits.py +382 -402
  141. floodmodeller_api/units/conveyance.py +301 -0
  142. floodmodeller_api/units/helpers.py +123 -131
  143. floodmodeller_api/units/iic.py +107 -101
  144. floodmodeller_api/units/losses.py +305 -306
  145. floodmodeller_api/units/sections.py +465 -446
  146. floodmodeller_api/units/structures.py +1690 -1683
  147. floodmodeller_api/units/units.py +93 -104
  148. floodmodeller_api/units/unsupported.py +44 -44
  149. floodmodeller_api/units/variables.py +87 -89
  150. floodmodeller_api/urban1d/__init__.py +11 -11
  151. floodmodeller_api/urban1d/_base.py +188 -179
  152. floodmodeller_api/urban1d/conduits.py +93 -85
  153. floodmodeller_api/urban1d/general_parameters.py +58 -58
  154. floodmodeller_api/urban1d/junctions.py +81 -79
  155. floodmodeller_api/urban1d/losses.py +81 -74
  156. floodmodeller_api/urban1d/outfalls.py +114 -110
  157. floodmodeller_api/urban1d/raingauges.py +111 -111
  158. floodmodeller_api/urban1d/subsections.py +92 -98
  159. floodmodeller_api/urban1d/xsections.py +147 -144
  160. floodmodeller_api/util.py +119 -21
  161. floodmodeller_api/validation/parameters.py +660 -660
  162. floodmodeller_api/validation/urban_parameters.py +388 -404
  163. floodmodeller_api/validation/validation.py +110 -108
  164. floodmodeller_api/version.py +1 -1
  165. floodmodeller_api/xml2d.py +632 -673
  166. floodmodeller_api/xml2d_template.py +37 -37
  167. floodmodeller_api/zzn.py +414 -363
  168. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/LICENSE.txt +13 -13
  169. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/METADATA +85 -82
  170. floodmodeller_api-0.4.4.dist-info/RECORD +185 -0
  171. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/WHEEL +1 -1
  172. floodmodeller_api/libifcoremd.dll +0 -0
  173. floodmodeller_api/test/test_data/EX3.bmp +0 -0
  174. floodmodeller_api/test/test_data/test_output.csv +0 -87
  175. floodmodeller_api/zzn_read.dll +0 -0
  176. floodmodeller_api-0.4.2.post1.dist-info/RECORD +0 -164
  177. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/entry_points.txt +0 -0
  178. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/top_level.txt +0 -0
@@ -1,92 +1,222 @@
1
- import os
2
- from pathlib import Path
3
-
4
- import pytest
5
-
6
- from floodmodeller_api import DAT
7
- from floodmodeller_api.units import QTBDY
8
-
9
-
10
- @pytest.fixture
11
- def dat_fp(test_workspace):
12
- return os.path.join(test_workspace, "network.DAT")
13
-
14
-
15
- @pytest.fixture
16
- def data_before(dat_fp):
17
- return DAT(dat_fp)._write()
18
-
19
-
20
- def test_dat_str_not_changed_by_write(dat_fp, data_before):
21
- # TODO: Update this test - it isn't really testing anything since the behaviour of the fixture is exactly the same
22
- """DAT: Test str representation equal to dat file with no changes"""
23
- dat = DAT(dat_fp)
24
- assert dat._write() == data_before
25
-
26
-
27
- def test_changing_section_and_dist_works(dat_fp, data_before):
28
- """DAT: Test changing and reverting section name and dist to next makes no changes"""
29
- dat = DAT(dat_fp)
30
- prev_name = dat.sections["CSRD10"].name
31
- prev_dist = dat.sections["CSRD10"].dist_to_next
32
- dat.sections["CSRD10"].name = "check"
33
- dat.sections["CSRD10"].dist_to_next = 0.0
34
- assert dat._write() != data_before
35
-
36
- dat.sections["check"].name = prev_name
37
- dat.sections["check"].dist_to_next = prev_dist
38
- assert dat._write() == data_before
39
-
40
-
41
- def test_changing_and_reverting_qtbdy_hydrograph_works(dat_fp, data_before):
42
- """DAT: Test changing and reverting QTBDY hydrograph makes no changes"""
43
- dat = DAT(dat_fp)
44
- prev_qt = {}
45
- for name, unit in dat.boundaries.items():
46
- if isinstance(unit, QTBDY):
47
- prev_qt[name] = unit.data.copy()
48
- unit.data *= 2 # Multiply QT flow data by 2
49
- assert dat._write() != data_before
50
-
51
- for name, qt in prev_qt.items():
52
- dat.boundaries[name].data = qt # replace QT flow data with original
53
- assert dat._write() == data_before
54
-
55
-
56
- def test_dat_read_doesnt_change_data(test_workspace):
57
- """DAT: Check all '.dat' files in folder by reading the _write() output into a new DAT instance and checking it stays the same."""
58
- for datfile in Path(test_workspace).glob("*.dat"):
59
- dat = DAT(datfile)
60
- first_output = dat._write()
61
- dat.save("__temp.dat")
62
- second_dat = DAT("__temp.dat")
63
- assert dat == second_dat # Checks equivalence on the class itself
64
- second_output = second_dat._write()
65
- assert first_output == second_output
66
- os.remove("__temp.dat")
67
- try:
68
- os.remove("__temp.gxy")
69
- except FileNotFoundError:
70
- pass
71
-
72
-
73
- def test_insert_unit(test_workspace):
74
- dat_a = DAT(Path(test_workspace, "EX3.DAT"))
75
- dat_b = DAT(Path(Path(test_workspace, "EX6.DAT")))
76
- unit = dat_a.sections["20"]
77
- dat_b.insert_unit(unit, add_before=dat_b.sections["P4000"])
78
- # Check unit is added to sections
79
- assert "20" in dat_b.sections
80
- # Check unit in correct position in all units
81
- assert dat_b._all_units[8:10] == [unit, dat_b.sections["P4000"]]
82
-
83
-
84
- def test_remove_unit(test_workspace):
85
- dat_a = DAT(Path(test_workspace, "EX3.DAT"))
86
- unit = dat_a.sections["20"]
87
- prev_dat_struct_len = len(dat_a._dat_struct)
88
- dat_a.remove_unit(unit)
89
- assert "20" not in dat_a.sections
90
- assert unit not in dat_a._all_units
91
- assert dat_a._dat_struct
92
- assert (prev_dat_struct_len - len(dat_a._dat_struct)) == 1
1
+ from pathlib import Path
2
+ from unittest.mock import patch
3
+
4
+ import pytest
5
+
6
+ from floodmodeller_api import DAT
7
+ from floodmodeller_api.units import QTBDY
8
+ from floodmodeller_api.util import FloodModellerAPIError
9
+
10
+
11
+ @pytest.fixture
12
+ def dat_fp(test_workspace):
13
+ return Path(test_workspace, "network.dat")
14
+
15
+
16
+ @pytest.fixture
17
+ def data_before(dat_fp):
18
+ return DAT(dat_fp)._write()
19
+
20
+
21
+ @pytest.fixture()
22
+ def dat_ex3(test_workspace):
23
+ return DAT(Path(test_workspace, "EX3.DAT"))
24
+
25
+
26
+ @pytest.fixture()
27
+ def units(dat_ex3):
28
+ unit_1 = dat_ex3.sections["20"]
29
+ unit_2 = dat_ex3.sections["40"]
30
+ unit_3 = dat_ex3.sections["60"]
31
+ return [unit_1, unit_2, unit_3]
32
+
33
+
34
+ @pytest.fixture()
35
+ def dat_ex6(test_workspace):
36
+ dat = DAT(Path(test_workspace, "EX6.DAT"))
37
+ with (
38
+ patch.object(dat, "_update_raw_data", wraps=dat._update_raw_data),
39
+ patch.object(dat, "_update_dat_struct", wraps=dat._update_dat_struct),
40
+ ):
41
+ yield dat
42
+
43
+
44
+ def test_dat_str_not_changed_by_write(dat_fp, data_before):
45
+ # TODO: Update this test - it isn't really testing anything since the behaviour of the fixture is exactly the same
46
+ """DAT: Test str representation equal to dat file with no changes"""
47
+ dat = DAT(dat_fp)
48
+ assert dat._write() == data_before
49
+
50
+
51
+ def test_changing_section_and_dist_works(dat_fp, data_before):
52
+ """DAT: Test changing and reverting section name and dist to next makes no changes"""
53
+ dat = DAT(dat_fp)
54
+ prev_name = dat.sections["CSRD10"].name
55
+ prev_dist = dat.sections["CSRD10"].dist_to_next
56
+ dat.sections["CSRD10"].name = "check"
57
+ dat.sections["CSRD10"].dist_to_next = 0.0
58
+ assert dat._write() != data_before
59
+
60
+ dat.sections["check"].name = prev_name
61
+ dat.sections["check"].dist_to_next = prev_dist
62
+ assert dat._write() == data_before
63
+
64
+
65
+ def test_changing_and_reverting_qtbdy_hydrograph_works(dat_fp, data_before):
66
+ """DAT: Test changing and reverting QTBDY hydrograph makes no changes"""
67
+ dat = DAT(dat_fp)
68
+ prev_qt = {}
69
+ for name, unit in dat.boundaries.items():
70
+ if isinstance(unit, QTBDY):
71
+ prev_qt[name] = unit.data.copy()
72
+ unit.data *= 2 # Multiply QT flow data by 2
73
+ assert dat._write() != data_before
74
+
75
+ for name, qt in prev_qt.items():
76
+ dat.boundaries[name].data = qt # replace QT flow data with original
77
+ assert dat._write() == data_before
78
+
79
+
80
+ def test_dat_read_doesnt_change_data(test_workspace, tmpdir):
81
+ """DAT: Check all '.dat' files in folder by reading the _write() output into a new DAT instance and checking it stays the same."""
82
+ for datfile in Path(test_workspace).glob("*.dat"):
83
+ dat = DAT(datfile)
84
+ first_output = dat._write()
85
+ new_path = Path(tmpdir) / "tmp.dat"
86
+ dat.save(new_path)
87
+ second_dat = DAT(new_path)
88
+ assert dat == second_dat # Checks equivalence on the class itself
89
+ second_output = second_dat._write()
90
+ assert first_output == second_output
91
+
92
+
93
+ def test_insert_unit_before(units, dat_ex6):
94
+ dat_ex6.insert_unit(units[0], add_before=dat_ex6.sections["P4000"])
95
+ assert "20" in dat_ex6.sections
96
+ assert dat_ex6._all_units[8:10] == [units[0], dat_ex6.sections["P4000"]]
97
+ dat_ex6._update_raw_data.assert_called_once()
98
+ dat_ex6._update_dat_struct.assert_called_once()
99
+
100
+
101
+ def test_insert_unit_after(units, dat_ex6):
102
+ dat_ex6.insert_unit(units[0], add_after=dat_ex6.sections["P4000"])
103
+ assert "20" in dat_ex6.sections
104
+ assert dat_ex6._all_units[8:10] == [dat_ex6.sections["P4000"], units[0]]
105
+ dat_ex6._update_raw_data.assert_called_once()
106
+ dat_ex6._update_dat_struct.assert_called_once()
107
+
108
+
109
+ def test_insert_unit_at(units, dat_ex6):
110
+ dat_ex6.insert_unit(units[0], add_at=2)
111
+ assert "20" in dat_ex6.sections
112
+ assert dat_ex6._all_units[2] == units[0]
113
+ dat_ex6._update_raw_data.assert_called_once()
114
+ dat_ex6._update_dat_struct.assert_called_once()
115
+
116
+
117
+ @pytest.mark.parametrize(
118
+ "kwargs",
119
+ [
120
+ {"add_at": 1, "add_before": 2},
121
+ {"add_at": 1, "add_after": 3},
122
+ {"add_before": 2, "add_after": 3},
123
+ {"add_at": 1, "add_before": 2, "add_after": 3},
124
+ ],
125
+ )
126
+ def test_insert_unit_too_many_arguments_error(dat_ex6, units, kwargs):
127
+ msg = (
128
+ r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
129
+ r"\nAPI Error: Problem encountered when trying to insert unit into DAT file .*\."
130
+ r"\n"
131
+ r"\nDetails: .*-floodmodeller_api/dat\.py-\d+"
132
+ r"\nMsg: Only one of add_at, add_before, or add_after required"
133
+ r"\n"
134
+ r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
135
+ )
136
+ with pytest.raises(FloodModellerAPIError, match=msg):
137
+ dat_ex6.insert_unit(units[0], **kwargs)
138
+
139
+
140
+ def test_insert_unit_no_arguments_error(dat_ex6, units):
141
+ msg = (
142
+ r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
143
+ r"\nAPI Error: Problem encountered when trying to insert unit into DAT file .*\."
144
+ r"\n"
145
+ r"\nDetails: .*-floodmodeller_api/dat\.py-\d+"
146
+ r"\nMsg: No positional argument given\. Please provide either add_before, add_at or add_after"
147
+ r"\n"
148
+ r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
149
+ )
150
+ with pytest.raises(FloodModellerAPIError, match=msg):
151
+ dat_ex6.insert_unit(units[0])
152
+
153
+
154
+ def test_insert_units_before(units, dat_ex6):
155
+ dat_ex6.insert_units(units, add_before=dat_ex6.sections["P4000"])
156
+ assert "20" in dat_ex6.sections
157
+ assert "40" in dat_ex6.sections
158
+ assert "60" in dat_ex6.sections
159
+ assert dat_ex6._all_units[8:12] == [*units, dat_ex6.sections["P4000"]]
160
+ dat_ex6._update_raw_data.assert_called_once()
161
+ dat_ex6._update_dat_struct.assert_called_once()
162
+
163
+
164
+ def test_insert_units_after(units, dat_ex6):
165
+ dat_ex6.insert_units(units, add_after=dat_ex6.sections["P4000"])
166
+ assert "20" in dat_ex6.sections
167
+ assert "40" in dat_ex6.sections
168
+ assert "60" in dat_ex6.sections
169
+ assert dat_ex6._all_units[8:12] == [dat_ex6.sections["P4000"], *units]
170
+ dat_ex6._update_raw_data.assert_called_once()
171
+ dat_ex6._update_dat_struct.assert_called_once()
172
+
173
+
174
+ def test_insert_units_at(units, dat_ex6):
175
+ dat_ex6.insert_units(units, add_at=2)
176
+ assert "20" in dat_ex6.sections
177
+ assert "40" in dat_ex6.sections
178
+ assert "60" in dat_ex6.sections
179
+ assert dat_ex6._all_units[2:5] == units
180
+ dat_ex6._update_raw_data.assert_called_once()
181
+ dat_ex6._update_dat_struct.assert_called_once()
182
+
183
+
184
+ def test_insert_units_at_end(units, dat_ex6):
185
+ dat_ex6.insert_units(units, add_at=-1)
186
+ assert "20" in dat_ex6.sections
187
+ assert "40" in dat_ex6.sections
188
+ assert "60" in dat_ex6.sections
189
+ assert dat_ex6._all_units[-3:] == units
190
+ dat_ex6._update_raw_data.assert_called_once()
191
+ dat_ex6._update_dat_struct.assert_called_once()
192
+
193
+
194
+ def test_remove_unit(dat_ex3):
195
+ unit = dat_ex3.sections["20"]
196
+ prev_dat_struct_len = len(dat_ex3._dat_struct)
197
+ dat_ex3.remove_unit(unit)
198
+ assert "20" not in dat_ex3.sections
199
+ assert unit not in dat_ex3._all_units
200
+ assert dat_ex3._dat_struct
201
+ assert (prev_dat_struct_len - len(dat_ex3._dat_struct)) == 1
202
+
203
+
204
+ def test_diff(test_workspace, capsys):
205
+ dat_ex4 = DAT(Path(test_workspace, "ex4.DAT"))
206
+ dat_ex4_changed = DAT(Path(test_workspace, "ex4_changed.DAT"))
207
+ dat_ex4.diff(dat_ex4_changed)
208
+ assert capsys.readouterr().out == (
209
+ "Files not equivalent, 12 difference(s) found:\n"
210
+ " DAT->structures->MILLAu->RNWEIR..MILLAu->upstream_crest_height: 1.07 != 1.37\n"
211
+ " DAT->structures->MILLBu->RNWEIR..MILLBu->upstream_crest_height: 0.43 != 0.73\n"
212
+ " DAT->structures->ROAD1->RNWEIR..ROAD1->upstream_crest_height: 2.02 != 2.32\n"
213
+ " DAT->structures->RAILRDu->RNWEIR..RAILRDu->upstream_crest_height: 1.75 != 2.05\n"
214
+ " DAT->structures->CSRD01u->RNWEIR..CSRD01u->upstream_crest_height: 0.81 != 1.11\n"
215
+ " DAT->structures->FOOTa->RNWEIR..FOOTa->upstream_crest_height: 2.47 != 2.77\n"
216
+ " DAT->_all_units->itm[28]->RNWEIR..MILLAu->upstream_crest_height: 1.07 != 1.37\n"
217
+ " DAT->_all_units->itm[29]->RNWEIR..MILLBu->upstream_crest_height: 0.43 != 0.73\n"
218
+ " DAT->_all_units->itm[42]->RNWEIR..ROAD1->upstream_crest_height: 2.02 != 2.32\n"
219
+ " DAT->_all_units->itm[57]->RNWEIR..RAILRDu->upstream_crest_height: 1.75 != 2.05\n"
220
+ " DAT->_all_units->itm[61]->RNWEIR..CSRD01u->upstream_crest_height: 0.81 != 1.11\n"
221
+ " DAT->_all_units->itm[73]->RNWEIR..FOOTa->upstream_crest_height: 2.47 != 2.77\n"
222
+ )