-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite expect script in C to avoid multiple issues with shell
- Loading branch information
Showing
5 changed files
with
215 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,46 @@ | ||
#!/bin/sh -eu | ||
|
||
script_dir=$(dirname "$0") | ||
# The Makefile target which will be the test name | ||
TARGET="$1" | ||
|
||
export TARGET="$1" | ||
# The intermediate test stage if the test is split into multiple stages | ||
# if it is blank then it is the last stage | ||
if [ -z "${2:-}" ]; then | ||
export STAGE="" | ||
STAGE="" | ||
else | ||
export STAGE=-"$2" | ||
STAGE=-"$2" | ||
fi | ||
export CAPTURE="${TMP_DIR}/$TARGET$STAGE".out | ||
EXPECTED="$EXPECTED_PATH/$TARGET$STAGE".out | ||
export EXPECTED | ||
matched=false | ||
|
||
t=${EXPECT_TIMEOUT:-5} | ||
|
||
cleanup() { | ||
if $matched; then | ||
if [ -z "$STAGE" ]; then | ||
tmux send-keys -t "$TARGET" "q" | ||
fi | ||
exit 0 | ||
fi | ||
|
||
tmux send-keys -t "$TARGET" "q" | ||
echo 'Incorrect output:' | ||
cat "$CAPTURE" | ||
${CMP} -s "$CAPTURE" "$EXPECTED" | ||
exit 1 | ||
} | ||
|
||
trap cleanup INT TERM QUIT | ||
# The capture file that is written to if we fail to match. If the capture is correct | ||
# but expected is missing or wrong, then we can copy this to the expected location | ||
CAPTURE="${TMP_DIR}/$TARGET$STAGE".out | ||
# The expected output to match against | ||
EXPECTED="$EXPECTED_PATH/$TARGET$STAGE".out | ||
|
||
printf "\n%s, %s" "$TARGET" "${2:-}" >> "${TIMINGS_CSV}" | ||
# write Git hash, target, stage, date, time to the CSV timings file | ||
printf "\n%s, %s, %s, %s" "$(git rev-parse --short HEAD)" "$TARGET" "${2:-}" "$(date '+%F, %T')" >> "${TIMINGS_CSV}" | ||
|
||
# temporarily disable error checking | ||
set +e | ||
match_time=$(time -p timeout -k $(( t + 1 )) $t "${script_dir}"/test-retry-capture-cmp.sh 2>&1) | ||
# run the expect utility which will repeatedly | ||
# try to match the expected output to the screen captured by tmux. If successfull | ||
# it will output the elapsed time to stdout and therefor match_time, on failure it | ||
# will print to stderr which the user will see | ||
match_time=$($EXPECT_BIN "$EXPECTED" "$TARGET" "$CAPTURE" "$EXPECT_TIMEOUT") | ||
status=$? | ||
set -e | ||
|
||
if [ $status -eq 0 ]; then | ||
matched=true | ||
match_time=$(echo "$match_time" | head -n 1 | cut -f 2 -d ' ') | ||
# write the time it took to match the expected text with the capture | ||
echo "$TARGET$STAGE took $match_time" | ||
printf ", %s" "$match_time" >> "${TIMINGS_CSV}" | ||
else | ||
echo "$TARGET$STAGE did not match" | ||
fi | ||
|
||
# Quit if this is the last stage or there was an error | ||
if [ -z "$STAGE" ] || [ $status -eq 1 ]; then | ||
tmux send-keys -t "$TARGET" "q" | ||
fi | ||
|
||
cleanup | ||
exit $status |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <time.h> | ||
|
||
/* | ||
* This utility allows us to wait for a particular output from zsv sheet. | ||
* | ||
* It avoids needing to call sleep with a set time and instead we specify the output | ||
* we wish to see and a timeout. If the timeout is exceeded then it fails and prints | ||
* the output that was last seen and saves it to a file. | ||
* | ||
* It repeatedly calls tmux capture-pane and compares the contents with the expected file. | ||
* | ||
* There is a wrapper for this utility which is used in the Makefiles scripts/test-expect.sh | ||
*/ | ||
|
||
int main(int argc, char **argv) { | ||
if (argc != 5) { | ||
fprintf(stderr, "Usage: %s <expected> <pane> <actual> <timeout>\n", argv[0]); | ||
return 1; | ||
} | ||
|
||
char *expected = argv[1]; | ||
char *pane = argv[2]; | ||
char *actual = argv[3]; | ||
char *timeout_s = argv[4]; | ||
|
||
char *endptr; | ||
double timeout = strtod(timeout_s, &endptr); | ||
|
||
if (endptr == timeout_s) { | ||
perror("strtod"); | ||
return 1; | ||
} | ||
|
||
// Read the contents of the file | ||
FILE *file = fopen(expected, "r"); | ||
if (file == NULL) { | ||
perror("fopen"); | ||
return 1; | ||
} | ||
|
||
if (fseek(file, 0, SEEK_END) != 0) { | ||
perror("fseek"); | ||
return 1; | ||
} | ||
|
||
long file_size = ftell(file); | ||
if (file_size < 0) { | ||
perror("ftell"); | ||
return 1; | ||
} | ||
|
||
rewind(file); | ||
|
||
char *expected_output = malloc(file_size + 1); | ||
if (expected_output == NULL) { | ||
perror("malloc"); | ||
return 1; | ||
} | ||
|
||
size_t bytes_read = fread(expected_output, 1, file_size, file); | ||
if (bytes_read != (size_t)file_size) { | ||
if (ferror(file)) { | ||
perror("fread"); | ||
return 1; | ||
} | ||
} | ||
|
||
expected_output[file_size] = '\0'; | ||
|
||
if (fclose(file) != 0) { | ||
perror("fclose"); | ||
return 1; | ||
} | ||
|
||
struct timespec start_time; | ||
if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) { | ||
perror("clock_gettime"); | ||
return 1; | ||
} | ||
|
||
struct timespec current_time; | ||
char *expected_input = malloc(file_size + 1); | ||
if (expected_input == NULL) { | ||
perror("malloc"); | ||
return 1; | ||
} | ||
|
||
double elapsed_time; | ||
char command[256]; | ||
snprintf(command, sizeof(command), "tmux capture-pane -t %s -p", pane); | ||
|
||
while (1) { | ||
// Run tmux capture-pane -p | ||
FILE *pipe = popen(command, "r"); | ||
if (pipe == NULL) { | ||
perror("popen"); | ||
return 1; | ||
} | ||
|
||
size_t len = 0; | ||
for (int i = 0; i < 10 && len < (size_t)file_size; i++) { | ||
len += fread(expected_input + len, 1, file_size - len, pipe); | ||
if (ferror(pipe)) { | ||
perror("fread"); | ||
return 1; | ||
} | ||
usleep(1); | ||
} | ||
|
||
if (pclose(pipe) != 0) { | ||
return 1; | ||
} | ||
|
||
// Check if the timeout has expired | ||
if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) { | ||
perror("clock_gettime"); | ||
return 1; | ||
} | ||
|
||
elapsed_time = | ||
(current_time.tv_sec - start_time.tv_sec) + (current_time.tv_nsec - start_time.tv_nsec) / 1000000000.0; | ||
|
||
// Check if the output matches the expected output | ||
if (strcmp(expected_input, expected_output) == 0) { | ||
break; | ||
} | ||
|
||
if (elapsed_time > timeout) { | ||
fprintf(stderr, "Timeout expired after %.2f seconds\n", elapsed_time); | ||
fprintf(stderr, "Last output that failed to match:\n%s\n", expected_input); | ||
|
||
FILE *output = fopen(actual, "w"); | ||
if (output == NULL) { | ||
perror("fopen"); | ||
return 1; | ||
} | ||
|
||
fprintf(output, "%s", expected_input); | ||
fclose(output); | ||
|
||
return 1; | ||
} | ||
|
||
// Sleep for a short period of time before trying again | ||
struct timespec sleep_time = {0, 10000000}; // 10 milliseconds | ||
if (nanosleep(&sleep_time, NULL) != 0) { | ||
perror("nanosleep"); | ||
return 1; | ||
} | ||
} | ||
|
||
printf("%.2f\n", elapsed_time); | ||
|
||
return 0; | ||
} |