Blame
|
1 | <!-- Generated from applied-computing-au vic/unit3-4/sat/C07-2026/C07-Reference-Godot by port-reference-godot-to-wiki.py — do not hand-edit; re-run the port. --> |
||||||
| 2 | # Validation Techniques — GDScript |
|||||||
| 3 | ||||||||
| 4 | **Skill codes on this page** |
|||||||
| 5 | ||||||||
| 6 | | Code | Level | Skill | |
|||||||
| 7 | |---|---|---| |
|||||||
| 8 | | C713 | 1–2 | identifies input data for validation | |
|||||||
| 9 | | C723 | 3–4 | existence checking | |
|||||||
| 10 | | C724 | 3–4 | type checking | |
|||||||
| 11 | | C725 | 3–4 | range checking | |
|||||||
| 12 | | C735 | 5–6 | two of the three checks | |
|||||||
| 13 | | C745 | 7–8 | all three checks | |
|||||||
| 14 | | C753 ✍️ | 9–10 | ALL input data, plus reasonableness and completeness | |
|||||||
| 15 | ||||||||
| 16 | **Definition.** *Validation* checks the reasonableness of data inputs, while the program runs. It is not testing (testing happens to the finished program; validation happens to every input, every run). The three named checks: **existence** (was anything entered?), **type** (is it the right kind of value?), **range** (is it within acceptable limits?). |
|||||||
| 17 | ||||||||
| 18 | **This ladder is not a checklist.** The individual checks are separate codes only at level 3–4. Above that, single codes stand for the *combination*: C735 = two checks working, C745 = all three, C753 = all inputs plus reasonableness and completeness. Consistency qualifiers decide the band: *some inconsistencies* (5–6) → *minor* (7–8) → *none* (9–10). |
|||||||
| 19 | ||||||||
| 20 | **In the validation** Part B you add existence/type/range checks to the picked feature's real inputs, label them as you go, and **run a test to show a check actually fires** — a validation that never catches a bad value is scenery. |
|||||||
| 21 | ||||||||
| 22 | ## C713 — Identify the inputs |
|||||||
| 23 | ||||||||
| 24 | Level 1–2 is a written list of what needs validating: |
|||||||
| 25 | ||||||||
| 26 | ```gdscript |
|||||||
| 27 | # C713 — inputs to validate in this feature: |
|||||||
| 28 | # team name — NameField (LineEdit), free text |
|||||||
| 29 | # zone — ZoneSpinBox (SpinBox), whole number |
|||||||
| 30 | # top-up — TopUpField (LineEdit), dollars and cents |
|||||||
| 31 | ``` |
|||||||
| 32 | ||||||||
| 33 | **Earns the tick:** every user-facing input of the picked feature named. (Choosing constrained controls — a SpinBox can't receive letters — is good design, but the codes below want checks *in code*.) |
|||||||
| 34 | ||||||||
| 35 | ## C723 — Existence check |
|||||||
| 36 | ||||||||
| 37 | **Definition.** A test to see if a value has been entered as input or not. |
|||||||
| 38 | ||||||||
| 39 | ```gdscript |
|||||||
| 40 | # club-ladder — app.gd |
|||||||
| 41 | func add_team(raw_name: String) -> void: |
|||||||
| 42 | var team_name := raw_name.strip_edges() |
|||||||
| 43 | if team_name == "": # C723 — existence: nothing entered |
|||||||
| 44 | message_label.text = "Type a team name first." |
|||||||
| 45 | return |
|||||||
| 46 | ``` |
|||||||
| 47 | ||||||||
| 48 | Note the real-world details worth copying: `strip_edges()` first (spaces are not a name), and an error message that says **what to do**, not just "invalid". |
|||||||
| 49 | ||||||||
| 50 | ## C724 — Type check |
|||||||
| 51 | ||||||||
| 52 | **Definition.** A test to see if a value is of the specified data type or structure. |
|||||||
| 53 | ||||||||
| 54 | GDScript's idioms — check *before* converting, or the conversion itself is the crash: |
|||||||
| 55 | ||||||||
| 56 | ```gdscript |
|||||||
| 57 | # C724 — type: is the text actually a number? |
|||||||
| 58 | if not $TopUpField.text.is_valid_float(): |
|||||||
| 59 | $MessageLabel.text = "Enter an amount in dollars, like 10.50" |
|||||||
| 60 | return |
|||||||
| 61 | var amount := float($TopUpField.text) |
|||||||
| 62 | ``` |
|||||||
| 63 | ||||||||
| 64 | `String.is_valid_int()` and `is_valid_float()` are the workhorses. The `is` keyword type-checks objects — real use in tile-matching-game: `if child is Card:` (`board.gd`). |
|||||||
| 65 | ||||||||
| 66 | ## C725 — Range check |
|||||||
| 67 | ||||||||
| 68 | **Definition.** Tests to see if a value is within a given range of acceptable values. |
|||||||
| 69 | ||||||||
| 70 | ```gdscript |
|||||||
| 71 | # C725 — range: zones only run 1..3, amounts have a ceiling |
|||||||
| 72 | if zone < 1 or zone > MAX_ZONE: |
|||||||
| 73 | $MessageLabel.text = "Zone must be 1 to %d" % MAX_ZONE |
|||||||
| 74 | return |
|||||||
| 75 | if amount <= 0.0 or amount > MAX_TOP_UP: |
|||||||
| 76 | $MessageLabel.text = "Top-up must be between $0.01 and $%.2f" % MAX_TOP_UP |
|||||||
| 77 | return |
|||||||
| 78 | ``` |
|||||||
| 79 | ||||||||
| 80 | The limits are **named constants** (`MAX_ZONE`, `MAX_TOP_UP`) — that's C7-1 working for C7-3. Clamping (`maxi(1, column_count)` in club-ladder's `board.gd`) is the accept-and-correct cousin of reject-and-message; for user input, prefer the message — silent correction hides the user's mistake from them. |
|||||||
| 81 | ||||||||
| 82 | ## C735 and C745 — The checks working together |
|||||||
| 83 | ||||||||
| 84 | One validated input, all three checks, in the order that cannot crash — existence, then type, then range: |
|||||||
| 85 | ||||||||
| 86 | ```gdscript |
|||||||
| 87 | # C745 — full validation of the top-up amount (existence → type → range) |
|||||||
| 88 | func validate_top_up(raw: String) -> String: |
|||||||
| 89 | var text := raw.strip_edges() |
|||||||
| 90 | if text == "": # C723 — existence |
|||||||
| 91 | return "Enter an amount first." |
|||||||
| 92 | if not text.is_valid_float(): # C724 — type |
|||||||
| 93 | return "Enter a number, like 10.50" |
|||||||
| 94 | var amount := float(text) |
|||||||
| 95 | if amount <= 0.0 or amount > MAX_TOP_UP: # C725 — range |
|||||||
| 96 | return "Amount must be between $0.01 and $%.2f" % MAX_TOP_UP |
|||||||
| 97 | return "" # empty string = valid |
|||||||
| 98 | ``` |
|||||||
| 99 | ||||||||
| 100 | C735 (5–6) is two of these working on real inputs; C745 (7–8) is all three, with at most *minor* inconsistencies. **Error messages are part of the standard** — "Must be between 8 and 72 in length" beats "Invalid input" because it tells the user how to fix it (that Discord message was the class's best-in-show in the Validation Detective activity). |
|||||||
| 101 | ||||||||
| 102 | ## C753 — All inputs, plus reasonableness and completeness ✍️ |
|||||||
| 103 | ||||||||
| 104 | Level 9–10 widens the lens from single fields to the **whole data set**: |
|||||||
| 105 | ||||||||
| 106 | - **All relevant inputs** validated — not just the easy one. |
|||||||
| 107 | ||||||||
| 108 | - **Completeness** — is anything missing? Real example: `deck.gd` skips half-empty CSV rows with `if row.size() < 2: continue` (tile-matching-game). |
|||||||
| 109 | ||||||||
| 110 | - **Reasonableness** — can these values be true *together*? The model is club-ladder's cross-check: |
|||||||
| 111 | ||||||||
| 112 | ```gdscript |
|||||||
| 113 | # club-ladder — app.gd |
|||||||
| 114 | ## In a competition where teams play each other, one team's win is always # C753 ✍️ |
|||||||
| 115 | ## another's loss — so total wins must equal total losses. Worth saying on |
|||||||
| 116 | ## screen rather than quietly showing figures that cannot be true. |
|||||||
| 117 | func totals_warning() -> String: |
|||||||
| 118 | if total_wins == total_losses: |
|||||||
| 119 | return "" |
|||||||
| 120 | return "Check your data: %d wins recorded, but %d losses." % [total_wins, total_losses] |
|||||||
| 121 | ``` |
|||||||
| 122 | ||||||||
| 123 | **Earns the tick:** every input covered, a completeness check on incoming records, at least one reasonableness rule that crosses fields — and (✍️) the rule *written down*: work out what rules **your** data has to obey, and say so in a comment. |
|||||||
| 124 | ||||||||
| 125 | **Then prove it fires.** End the validation task by entering a bad value and showing the message appear — the package script asks for exactly this test run. |
|||||||
| 126 | ||||||||
| 127 | ## Check Your Understanding |
|||||||
| 128 | ||||||||
| 129 | 1. Name the three validation checks and their level 3–4 codes. |
|||||||
| 130 | ||||||||
| 131 | >| ### Answer |
|||||||
| 132 | >| Existence C723, type C724, range C725. |
|||||||
| 133 | ||||||||
| 134 | 2. Why are C735 and C745 single codes rather than lists of checks? |
|||||||
| 135 | ||||||||
| 136 | >| ### Answer |
|||||||
| 137 | >| Above level 3–4 the descriptor rewards the *combination* — two checks working (C735), then all three (C745) — with consistency qualifiers deciding the band. |
|||||||
| 138 | ||||||||
| 139 | 3. What makes "Must be between 8 and 72 in length" a better error message than "Invalid input"? |
|||||||
| 140 | ||||||||
| 141 | >| ### Answer |
|||||||
| 142 | >| It tells the user exactly how to fix the problem — specific beats generic. |
|||||||
| 143 | ||||||||
| 144 | 4. Give an example of a reasonableness check (C753). |
|||||||
| 145 | ||||||||
| 146 | >| ### Answer |
|||||||
| 147 | >| A cross-field rule — e.g. club-ladder's "total wins must equal total losses"; values that are individually fine but cannot be true together. |
|||||||
