adventuregamestudio / ags

AGS editor and engine source code
Other
708 stars 159 forks source link

[ags4] add to String Split the trim and empty removal options #2461

Closed ericoporto closed 5 months ago

ericoporto commented 5 months ago

fix #2458

This is an autotest game (fixed again!) ags4-auto-test-string-split.zip

It adds the following ported from the C# example in the issue (redone!) ``` // String.Split // String.Split { { String s = ",ONE,, TWO,, , THREE,,"; String result[] = s.Split(",", eStrSplit_None); tap.is_int(result.Length, 9, "String.Split: (1,length) by character delimiter without options"); tap.is(result[0], "", "String.Split: (1,element check 0) by character delimiter without options"); tap.is(result[1], "ONE", "String.Split: (1,element check 1) by character delimiter without options"); tap.is(result[2], "", "String.Split: (1,element check 2) by character delimiter without options"); tap.is(result[3], " TWO", "String.Split: (1,element check 3) by character delimiter without options"); tap.is(result[4], "", "String.Split: (1,element check 4) by character delimiter without options"); tap.is(result[5], " ", "String.Split: (1,element check 5) by character delimiter without options"); tap.is(result[6], " THREE", "String.Split: (1,element check 6) by character delimiter without options"); tap.is(result[7], "", "String.Split: (1,element check 7) by character delimiter without options"); tap.is(result[8], "", "String.Split: (1,element check 8) by character delimiter without options"); } { String s = ",ONE,, TWO,, , THREE,,"; String result[] = s.Split(",", eStrSplit_Trim); tap.is_int(result.Length, 9, "String.Split: (2,length) by character delimiter with Trim option"); tap.is(result[0], "", "String.Split: (2,element check 0) by character delimiter with Trim option"); tap.is(result[1], "ONE", "String.Split: (2,element check 1) by character delimiter with Trim option"); tap.is(result[2], "", "String.Split: (2,element check 2) by character delimiter with Trim option"); tap.is(result[3], "TWO", "String.Split: (2,element check 3) by character delimiter with Trim option"); tap.is(result[4], "", "String.Split: (2,element check 4) by character delimiter with Trim option"); tap.is(result[5], "", "String.Split: (2,element check 5) by character delimiter with Trim option"); tap.is(result[6], "THREE", "String.Split: (2,element check 6) by character delimiter with Trim option"); tap.is(result[7], "", "String.Split: (2,element check 7) by character delimiter with Trim option"); tap.is(result[8], "", "String.Split: (2,element check 8) by character delimiter with Trim option"); } { String s = ",ONE,, TWO,, , THREE,,"; String result[] = s.Split(",", eStrSplit_RemoveEmpty); tap.is_int(result.Length, 4, "String.Split: (3,length) by character delimiter with RemoveEmpty option"); tap.is(result[0], "ONE", "String.Split: (3,element check 0) by character delimiter with RemoveEmpty option"); tap.is(result[1], " TWO", "String.Split: (3,element check 1) by character delimiter with RemoveEmpty option"); tap.is(result[2], " ", "String.Split: (3,element check 2) by character delimiter with RemoveEmpty option"); tap.is(result[3], " THREE", "String.Split: (3,element check 3) by character delimiter with RemoveEmpty option"); } { String s = ",ONE,, TWO,, , THREE ,,"; String result[] = s.Split(",", eStrSplit_Trim | eStrSplit_RemoveEmpty); tap.is_int(result.Length, 3, "String.Split: (4,length) by character delimiter with Trim and RemoveEmpty options"); tap.is(result[0], "ONE", "String.Split: (4,element check 0) by character delimiter with Trim and RemoveEmpty options"); tap.is(result[1], "TWO", "String.Split: (4,element check 1) by character delimiter with Trim and RemoveEmpty options"); tap.is(result[2], "THREE", "String.Split: (4,element check 2) by character delimiter with Trim and RemoveEmpty options"); } { String s = "[stop]ONE[stop] [stop]TWO [stop][stop] [stop]THREE[stop][stop] "; String result[] = s.Split("[stop]", eStrSplit_None); tap.is_int(result.Length, 9, "String.Split: (5,length) by string delimiter without options"); tap.is(result[0], "", "String.Split: (5,element check 0) by string delimiter without options"); tap.is(result[1], "ONE", "String.Split: (5,element check 1) by string delimiter without options"); tap.is(result[2], " ", "String.Split: (5,element check 2) by string delimiter without options"); tap.is(result[3], "TWO ", "String.Split: (5,element check 3) by string delimiter without options"); tap.is(result[4], "", "String.Split: (5,element check 4) by string delimiter without options"); tap.is(result[5], " ", "String.Split: (5,element check 5) by string delimiter without options"); tap.is(result[6], "THREE", "String.Split: (5,element check 6) by string delimiter without options"); tap.is(result[7], "", "String.Split: (5,element check 7) by string delimiter without options"); tap.is(result[8], " ", "String.Split: (5,element check 8) by string delimiter without options"); } { String s = "[stop]ONE[stop] [stop]TWO [stop][stop] [stop]THREE[stop][stop] "; String result[] = s.Split("[stop]", eStrSplit_Trim); tap.is_int(result.Length, 9, "String.Split: (6,length) by string delimiter with Trim option"); tap.is(result[0], "", "String.Split: (6,element check 0) by string delimiter with Trim option"); tap.is(result[1], "ONE", "String.Split: (6,element check 1) by string delimiter with Trim option"); tap.is(result[2], "", "String.Split: (6,element check 2) by string delimiter with Trim option"); tap.is(result[3], "TWO", "String.Split: (6,element check 3) by string delimiter with Trim option"); tap.is(result[4], "", "String.Split: (6,element check 4) by string delimiter with Trim option"); tap.is(result[5], "", "String.Split: (6,element check 5) by string delimiter with Trim option"); tap.is(result[6], "THREE", "String.Split: (6,element check 6) by string delimiter with Trim option"); tap.is(result[7], "", "String.Split: (6,element check 7) by string delimiter with Trim option"); tap.is(result[8], "", "String.Split: (6,element check 8) by string delimiter with Trim option"); } { String s = "[stop]ONE[stop] [stop]TWO [stop][stop] [stop]THREE[stop][stop] "; String result[] = s.Split("[stop]", eStrSplit_RemoveEmpty); tap.is_int(result.Length, 6, "String.Split: (7,length) by string delimiter with RemoveEmpty option"); tap.is(result[0], "ONE", "String.Split: (7,element check 0) by string delimiter with RemoveEmpty option"); tap.is(result[1], " ", "String.Split: (7,element check 1) by string delimiter with RemoveEmpty option"); tap.is(result[2], "TWO ", "String.Split: (7,element check 2) by string delimiter with RemoveEmpty option"); tap.is(result[3], " ", "String.Split: (7,element check 3) by string delimiter with RemoveEmpty option"); tap.is(result[4], "THREE", "String.Split: (7,element check 4) by string delimiter with RemoveEmpty option"); tap.is(result[5], " ", "String.Split: (7,element check 5) by string delimiter with RemoveEmpty option"); } { String s = "[stop]ONE[stop] [stop]TWO [stop][stop] [stop]THREE[stop][stop] "; String result[] = s.Split("[stop]", eStrSplit_Trim | eStrSplit_RemoveEmpty); tap.is_int(result.Length, 3, "String.Split: (8,length) by string delimiter with Trim and RemoveEmpty options"); tap.is(result[0], "ONE", "String.Split: (8,element check 0) by string delimiter with Trim and RemoveEmpty options"); tap.is(result[1], "TWO", "String.Split: (8,element check 1) by string delimiter with Trim and RemoveEmpty options"); tap.is(result[2], "THREE", "String.Split: (8,element check 2) by string delimiter with Trim and RemoveEmpty options"); } } ```

Tests are passing now

ivan-mogilko commented 5 months ago

Note that ags4 test game needs to be recompiled with the editor from latest ags4 branch, in order to fix the object pointers.

ericoporto commented 5 months ago

Note that ags4 test game needs to be recompiled

Yes, the tests in the CI will fail until a new ags4 version is released I guess, but the attached game in this PR contains tests specifically for the String Split things.

It looks like I am hitting an off by one error in some cases of the String Split but I can't figure it out why/where in the code is wrong yet... Edit: figured, but also a few tests were incorrect.

ericoporto commented 5 months ago

Managed to find the issue and fix. I also had errors in the previous tests, so I redid those. It seems it's working now, please test!

I think I can squash all Engine commits now to a single commit too.

ivan-mogilko commented 5 months ago

In regards to automatic tests, my strong suggestion is to test ALL the elements of the resulting array. Indeed that's more code, but that will ensure that resulting items are valid and no edge case was missed.

This may be done, for example, by a helper function that accepts a resulting array returned from Split, and a manually prepared array; then compares these array lengths and contents element by element.

ericoporto commented 5 months ago

I added a new auto-test game that now tests all elements and the test passes! :)

ivan-mogilko commented 5 months ago

Looks like empty arrays also work:

  String s = ",";
  String r[] = s.Split(",", eStrSplit_RemoveEmpty);
  Display("r = %p", r); // prints valid address
  Display("r.Length = %d", r.Length); // prints 0
ericoporto commented 5 months ago

Uhm, probably something else to add for testing! So it can't be done directly through scripting but the machinery in the engine is correct.

After this makes in an ags4 release I can add this to the auto test game.