mirror of
https://github.com/mikefarah/yq.git
synced 2026-06-27 07:27:49 +00:00
* Initial plan * Add string slicing support to yq Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/a8525fbb-77a7-4bb0-a3a7-b24f99ae8710 Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com> * Fix sliceStringNode signature and fix test descriptions/expressions Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/58726b13-68ae-4f93-971f-eb70459edcf4 Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com> * Update pkg/yqlib/operator_slice.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix array slice out-of-bounds panic with very negative indices Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/7c146762-d251-45fd-8555-2488f59fc57b Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com> * S2-S4: tighten lexer condition, fix doc header, add Unicode example Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/ec06083e-e20a-45d2-bf7e-4e1fa7be1073 Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com> * Fix spelling: multibyte -> multi-byte in Unicode test subdescription Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/6e7b304b-5b52-4e89-8bad-ba22813305c7 Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com> Co-authored-by: Mike Farah <mikefarah@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
218 lines
5.6 KiB
Go
218 lines
5.6 KiB
Go
package yqlib
|
|
|
|
import "testing"
|
|
|
|
var sliceArrayScenarios = []expressionScenario{
|
|
{
|
|
description: "Slicing arrays",
|
|
document: `[cat, dog, frog, cow]`,
|
|
expression: `.[1:3]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- dog\n- frog\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing arrays - without the first number",
|
|
subdescription: "Starts from the start of the array",
|
|
document: `[cat, dog, frog, cow]`,
|
|
expression: `.[:2]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- cat\n- dog\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing arrays - without the second number",
|
|
subdescription: "Finishes at the end of the array",
|
|
document: `[cat, dog, frog, cow]`,
|
|
expression: `.[2:]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- frog\n- cow\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing arrays - use negative numbers to count backwards from the end",
|
|
document: `[cat, dog, frog, cow]`,
|
|
expression: `.[1:-1]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- dog\n- frog\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Inserting into the middle of an array",
|
|
subdescription: "using an expression to find the index",
|
|
document: `[cat, dog, frog, cow]`,
|
|
expression: `(.[] | select(. == "dog") | key + 1) as $pos | .[0:($pos)] + ["rabbit"] + .[$pos:]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- cat\n- dog\n- rabbit\n- frog\n- cow\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
document: `[[cat, dog, frog, cow], [apple, banana, grape, mango]]`,
|
|
expression: `.[] | .[1:3]`,
|
|
expected: []string{
|
|
"D0, P[0], (!!seq)::- dog\n- frog\n",
|
|
"D0, P[1], (!!seq)::- banana\n- grape\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
description: "second index beyond array clamps",
|
|
document: `[cat]`,
|
|
expression: `.[:3]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- cat\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
description: "first index beyond array returns nothing",
|
|
document: `[cat]`,
|
|
expression: `.[3:]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::[]\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
document: `[[cat, dog, frog, cow], [apple, banana, grape, mango]]`,
|
|
expression: `.[] | .[-2:-1]`,
|
|
expected: []string{
|
|
"D0, P[0], (!!seq)::- frog\n",
|
|
"D0, P[1], (!!seq)::- grape\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
document: `[cat1, cat2, cat3, cat4, cat5, cat6, cat7, cat8, cat9, cat10, cat11]`,
|
|
expression: `.[10:11]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- cat11\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
document: `[cat1, cat2, cat3, cat4, cat5, cat6, cat7, cat8, cat9, cat10, cat11]`,
|
|
expression: `.[-11:-10]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- cat1\n",
|
|
},
|
|
},
|
|
{
|
|
// Regression test for https://issues.oss-fuzz.com/issues/438776028
|
|
// Negative second index that underflows after adjustment must
|
|
// clamp to zero, yielding an empty sequence.
|
|
skipDoc: true,
|
|
document: `[a, b, c]`,
|
|
expression: `.[0:-99999]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::[]\n",
|
|
},
|
|
},
|
|
{
|
|
// First-index underflow: without clamping, the loop starts at a
|
|
// negative index and panics on Content access.
|
|
skipDoc: true,
|
|
document: `[a, b, c]`,
|
|
expression: `.[-99999:3]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::- a\n- b\n- c\n",
|
|
},
|
|
},
|
|
{
|
|
// Both indices underflow: both clamp to zero, yielding an empty
|
|
// sequence.
|
|
skipDoc: true,
|
|
document: `[a, b, c]`,
|
|
expression: `.[-99999:-99998]`,
|
|
expected: []string{
|
|
"D0, P[], (!!seq)::[]\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing strings",
|
|
document: `country: Australia`,
|
|
expression: `.country[0:5]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::Austr\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing strings - without the second number",
|
|
subdescription: "Finishes at the end of the string",
|
|
document: `country: Australia`,
|
|
expression: `.country[5:]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::alia\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing strings - without the first number",
|
|
subdescription: "Starts from the start of the string",
|
|
document: `country: Australia`,
|
|
expression: `.country[:5]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::Austr\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing strings - use negative numbers to count backwards from the end",
|
|
subdescription: "Negative indices count from the end of the string",
|
|
document: `country: Australia`,
|
|
expression: `.country[-5:]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::ralia\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
document: `country: Australia`,
|
|
expression: `.country[1:-1]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::ustrali\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
document: `country: Australia`,
|
|
expression: `.country[:]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::Australia\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
description: "second index beyond string length clamps",
|
|
document: `country: Australia`,
|
|
expression: `.country[:100]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::Australia\n",
|
|
},
|
|
},
|
|
{
|
|
skipDoc: true,
|
|
description: "first index beyond string length returns empty string",
|
|
document: `country: Australia`,
|
|
expression: `.country[100:]`,
|
|
expected: []string{
|
|
"D0, P[country], (!!str)::\n",
|
|
},
|
|
},
|
|
{
|
|
description: "Slicing strings - Unicode",
|
|
subdescription: "Indices are rune-based, so multi-byte characters are handled correctly",
|
|
document: `greeting: héllo`,
|
|
expression: `.greeting[1:3]`,
|
|
expected: []string{
|
|
"D0, P[greeting], (!!str)::él\n",
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestSliceOperatorScenarios(t *testing.T) {
|
|
for _, tt := range sliceArrayScenarios {
|
|
testScenario(t, &tt)
|
|
}
|
|
documentOperatorScenarios(t, "slice-array", sliceArrayScenarios)
|
|
}
|