1+ #! /bin/sh
2+
3+ # Usage: compare-sse.sh expected.txt actual.txt
4+ # Compares two SSE files allowing fields to be in any order
5+ # but preserving order within same prefix types
6+
7+ # Function for comparing SSE outputs with error display
8+ compare_sse_with_output () {
9+ expected=" $1 "
10+ actual=" $2 "
11+
12+ # Call the main comparison logic
13+ compare_sse " $expected " " $actual " || {
14+ echo " Difference between expected and actual output:"
15+ echo " "
16+ diff -u " $expected " " $actual " || true
17+ return 1
18+ }
19+ return 0
20+ }
21+
22+ # Main comparison function
23+ compare_sse () {
24+ if [ $# -ne 2 ]; then
25+ echo " Usage: $0 expected.txt actual.txt" >&2
26+ exit 1
27+ fi
28+
29+ expected=" $1 "
30+ actual=" $2 "
31+
32+ if [ ! -f " $expected " ]; then
33+ echo " Expected file not found: $expected " >&2
34+ exit 1
35+ fi
36+
37+ if [ ! -f " $actual " ]; then
38+ echo " Actual file not found: $actual " >&2
39+ exit 1
40+ fi
41+
42+ # Use awk to parse and compare SSE events
43+ awk '
44+ BEGIN {
45+ RS = "\n\n\n" # Events separated by double newlines
46+ FS = "\n" # Fields separated by single newlines
47+ event_count_1 = 0
48+ event_count_2 = 0
49+ }
50+
51+ # Process first file (expected)
52+ NR == FNR {
53+ if (NF > 0) {
54+ event_count_1++
55+ delete data_fields
56+ delete other_fields
57+ data_count = 0
58+
59+ # Parse fields and separate data fields from others
60+ for (i = 1; i <= NF; i++) {
61+ if ($i != "") {
62+ # Extract prefix (everything before first colon)
63+ prefix = $i
64+ sub(/:.*/, "", prefix)
65+
66+ if (prefix == "data") {
67+ # Extract subgroup (first word after "data: ")
68+ subgroup = $i
69+ sub(/^data: /, "", subgroup)
70+ sub(/ .*$/, "", subgroup)
71+
72+ # Store data fields with subgroup for sorting
73+ data_count++
74+ data_fields[data_count] = $i
75+ data_subgroups[data_count] = subgroup
76+ } else {
77+ # Store non-data fields by prefix
78+ if (!(prefix in other_fields)) {
79+ other_fields[prefix] = ""
80+ }
81+ if (other_fields[prefix] != "") {
82+ other_fields[prefix] = other_fields[prefix] "\n"
83+ }
84+ other_fields[prefix] = other_fields[prefix] $i
85+ }
86+ }
87+ }
88+
89+ # Store event data
90+ for (prefix in other_fields) {
91+ events_1[event_count_1, prefix] = other_fields[prefix]
92+ }
93+
94+ # Sort and store data fields
95+ if (data_count > 0) {
96+ # Create sorting indices based on subgroup, preserving order within subgroups
97+ for (i = 1; i <= data_count; i++) {
98+ sort_indices[i] = i
99+ }
100+
101+ # Sort indices by subgroup name
102+ for (i = 1; i <= data_count; i++) {
103+ for (j = i + 1; j <= data_count; j++) {
104+ if (data_subgroups[sort_indices[i]] > data_subgroups[sort_indices[j]]) {
105+ temp = sort_indices[i]
106+ sort_indices[i] = sort_indices[j]
107+ sort_indices[j] = temp
108+ }
109+ }
110+ }
111+
112+ # Join data fields in sorted order
113+ sorted_data = ""
114+ for (i = 1; i <= data_count; i++) {
115+ if (sorted_data != "") {
116+ sorted_data = sorted_data "\n"
117+ }
118+ sorted_data = sorted_data data_fields[sort_indices[i]]
119+ }
120+ events_1[event_count_1, "data"] = sorted_data
121+ }
122+ }
123+ next
124+ }
125+
126+ # Process second file (actual)
127+ {
128+ if (NF > 0) {
129+ event_count_2++
130+ delete data_fields
131+ delete other_fields
132+ data_count = 0
133+
134+ # Parse fields and separate data fields from others
135+ for (i = 1; i <= NF; i++) {
136+ if ($i != "") {
137+ # Extract prefix (everything before first colon)
138+ prefix = $i
139+ sub(/:.*/, "", prefix)
140+
141+ if (prefix == "data") {
142+ # Extract subgroup (first word after "data: ")
143+ subgroup = $i
144+ sub(/^data: /, "", subgroup)
145+ sub(/ .*$/, "", subgroup)
146+
147+ # Store data fields with subgroup for sorting
148+ data_count++
149+ data_fields[data_count] = $i
150+ data_subgroups[data_count] = subgroup
151+ } else {
152+ # Store non-data fields by prefix
153+ if (!(prefix in other_fields)) {
154+ other_fields[prefix] = ""
155+ }
156+ if (other_fields[prefix] != "") {
157+ other_fields[prefix] = other_fields[prefix] "\n"
158+ }
159+ other_fields[prefix] = other_fields[prefix] $i
160+ }
161+ }
162+ }
163+
164+ # Store event data
165+ for (prefix in other_fields) {
166+ events_2[event_count_2, prefix] = other_fields[prefix]
167+ }
168+
169+ # Sort and store data fields
170+ if (data_count > 0) {
171+ # Create sorting indices based on subgroup, preserving order within subgroups
172+ for (i = 1; i <= data_count; i++) {
173+ sort_indices[i] = i
174+ }
175+
176+ # Sort indices by subgroup name
177+ for (i = 1; i <= data_count; i++) {
178+ for (j = i + 1; j <= data_count; j++) {
179+ if (data_subgroups[sort_indices[i]] > data_subgroups[sort_indices[j]]) {
180+ temp = sort_indices[i]
181+ sort_indices[i] = sort_indices[j]
182+ sort_indices[j] = temp
183+ }
184+ }
185+ }
186+
187+ # Join data fields in sorted order
188+ sorted_data = ""
189+ for (i = 1; i <= data_count; i++) {
190+ if (sorted_data != "") {
191+ sorted_data = sorted_data "\n"
192+ }
193+ sorted_data = sorted_data data_fields[sort_indices[i]]
194+ }
195+ events_2[event_count_2, "data"] = sorted_data
196+ }
197+ }
198+ }
199+
200+ END {
201+ # Compare event counts
202+ if (event_count_1 != event_count_2) {
203+ print "Event count mismatch: expected " event_count_1 ", got " event_count_2 > "/dev/stderr"
204+ exit 1
205+ }
206+
207+ # Compare each event
208+ for (e = 1; e <= event_count_1; e++) {
209+ # Collect all prefixes from both events
210+ delete all_prefixes
211+ for (key in events_1) {
212+ split(key, parts, SUBSEP)
213+ if (parts[1] == e) {
214+ all_prefixes[parts[2]] = 1
215+ }
216+ }
217+ for (key in events_2) {
218+ split(key, parts, SUBSEP)
219+ if (parts[1] == e) {
220+ all_prefixes[parts[2]] = 1
221+ }
222+ }
223+
224+ # Compare fields for each prefix
225+ for (prefix in all_prefixes) {
226+ key1 = e SUBSEP prefix
227+ key2 = e SUBSEP prefix
228+
229+ # Check if prefix exists in both
230+ if (!(key1 in events_1) && !(key2 in events_2)) {
231+ continue
232+ }
233+
234+ if (!(key1 in events_1)) {
235+ print "Event " e ": missing prefix \"" prefix "\" in expected" > "/dev/stderr"
236+ exit 1
237+ }
238+
239+ if (!(key2 in events_2)) {
240+ print "Event " e ": missing prefix \"" prefix "\" in actual" > "/dev/stderr"
241+ exit 1
242+ }
243+
244+ # Compare field content
245+ if (events_1[key1] != events_2[key2]) {
246+ print "Event " e ": mismatch in \"" prefix "\" fields" > "/dev/stderr"
247+ print "Expected:" > "/dev/stderr"
248+ print events_1[key1] > "/dev/stderr"
249+ print "Actual:" > "/dev/stderr"
250+ print events_2[key2] > "/dev/stderr"
251+ exit 1
252+ }
253+ }
254+ }
255+
256+ # All matches
257+ exit 0
258+ }
259+ ' " $expected " " $actual "
260+ }
261+
262+ # If script is called directly (not sourced), run the comparison
263+ if [ " ${0##*/ } " = " compare-sse.sh" ]; then
264+ compare_sse " $@ "
265+ fi
0 commit comments