#!/usr/bin/env bash # # map-reduce tests # # un-comment this to run the tests with the Go race detector. # RACE=-race if [[ "$OSTYPE" = "darwin"* ]] then if go version | grep 'go1.17.[012345]' then # -race with plug-ins on x86 MacOS 12 with # go1.17 before 1.17.6 sometimes crash. RACE= echo '*** Turning off -race since it may not work on a Mac' echo ' with ' `go version` fi fi ISQUIET=$1 maybe_quiet() { if [ "$ISQUIET" == "quiet" ]; then "$@" > /dev/null 2>&1 else "$@" fi } TIMEOUT=timeout TIMEOUT2="" if timeout 2s sleep 1 > /dev/null 2>&1 then : else if gtimeout 2s sleep 1 > /dev/null 2>&1 then TIMEOUT=gtimeout else # no timeout command TIMEOUT= echo '*** Cannot find timeout command; proceeding without timeouts.' fi fi if [ "$TIMEOUT" != "" ] then TIMEOUT2=$TIMEOUT TIMEOUT2+=" -k 2s 120s " TIMEOUT+=" -k 2s 45s " fi # run the test in a fresh sub-directory. rm -rf mr-tmp mkdir mr-tmp || exit 1 cd mr-tmp || exit 1 rm -f mr-* # make sure software is freshly built. (cd ../../mrapps && go clean) (cd .. && go clean) (cd ../../mrapps && go build $RACE -buildmode=plugin wc.go) || exit 1 (cd ../../mrapps && go build $RACE -buildmode=plugin indexer.go) || exit 1 (cd ../../mrapps && go build $RACE -buildmode=plugin mtiming.go) || exit 1 (cd ../../mrapps && go build $RACE -buildmode=plugin rtiming.go) || exit 1 (cd ../../mrapps && go build $RACE -buildmode=plugin jobcount.go) || exit 1 (cd ../../mrapps && go build $RACE -buildmode=plugin early_exit.go) || exit 1 (cd ../../mrapps && go build $RACE -buildmode=plugin crash.go) || exit 1 (cd ../../mrapps && go build $RACE -buildmode=plugin nocrash.go) || exit 1 (cd .. && go build $RACE mrcoordinator.go) || exit 1 (cd .. && go build $RACE mrworker.go) || exit 1 (cd .. && go build $RACE mrsequential.go) || exit 1 failed_any=0 ######################################################### # first word-count # generate the correct output ../mrsequential ../../mrapps/wc.so ../pg*txt || exit 1 sort mr-out-0 > mr-correct-wc.txt rm -f mr-out* echo '***' Starting wc test. maybe_quiet $TIMEOUT ../mrcoordinator ../pg*txt & pid=$! # give the coordinator time to create the sockets. sleep 1 # start multiple workers. (maybe_quiet $TIMEOUT ../mrworker ../../mrapps/wc.so) & (maybe_quiet $TIMEOUT ../mrworker ../../mrapps/wc.so) & (maybe_quiet $TIMEOUT ../mrworker ../../mrapps/wc.so) & # wait for the coordinator to exit. wait $pid # since workers are required to exit when a job is completely finished, # and not before, that means the job has finished. sort mr-out* | grep . > mr-wc-all if cmp mr-wc-all mr-correct-wc.txt then echo '---' wc test: PASS else echo '---' wc output is not the same as mr-correct-wc.txt echo '---' wc test: FAIL failed_any=1 fi # wait for remaining workers and coordinator to exit. wait ######################################################### # now indexer rm -f mr-* # generate the correct output ../mrsequential ../../mrapps/indexer.so ../pg*txt || exit 1 sort mr-out-0 > mr-correct-indexer.txt rm -f mr-out* echo '***' Starting indexer test. maybe_quiet $TIMEOUT ../mrcoordinator ../pg*txt & sleep 1 # start multiple workers maybe_quiet $TIMEOUT ../mrworker ../../mrapps/indexer.so & maybe_quiet $TIMEOUT ../mrworker ../../mrapps/indexer.so sort mr-out* | grep . > mr-indexer-all if cmp mr-indexer-all mr-correct-indexer.txt then echo '---' indexer test: PASS else echo '---' indexer output is not the same as mr-correct-indexer.txt echo '---' indexer test: FAIL failed_any=1 fi wait ######################################################### echo '***' Starting map parallelism test. rm -f mr-* maybe_quiet $TIMEOUT ../mrcoordinator ../pg*txt & sleep 1 maybe_quiet $TIMEOUT ../mrworker ../../mrapps/mtiming.so & maybe_quiet $TIMEOUT ../mrworker ../../mrapps/mtiming.so NT=`cat mr-out* | grep '^times-' | wc -l | sed 's/ //g'` if [ "$NT" != "2" ] then echo '---' saw "$NT" workers rather than 2 echo '---' map parallelism test: FAIL failed_any=1 fi if cat mr-out* | grep '^parallel.* 2' > /dev/null then echo '---' map parallelism test: PASS else echo '---' map workers did not run in parallel echo '---' map parallelism test: FAIL failed_any=1 fi wait ######################################################### echo '***' Starting reduce parallelism test. rm -f mr-* maybe_quiet $TIMEOUT ../mrcoordinator ../pg*txt & sleep 1 maybe_quiet $TIMEOUT ../mrworker ../../mrapps/rtiming.so & maybe_quiet $TIMEOUT ../mrworker ../../mrapps/rtiming.so NT=`cat mr-out* | grep '^[a-z] 2' | wc -l | sed 's/ //g'` if [ "$NT" -lt "2" ] then echo '---' too few parallel reduces. echo '---' reduce parallelism test: FAIL failed_any=1 else echo '---' reduce parallelism test: PASS fi wait ######################################################### echo '***' Starting job count test. rm -f mr-* maybe_quiet $TIMEOUT ../mrcoordinator ../pg*txt & sleep 1 maybe_quiet $TIMEOUT ../mrworker ../../mrapps/jobcount.so & maybe_quiet $TIMEOUT ../mrworker ../../mrapps/jobcount.so maybe_quiet $TIMEOUT ../mrworker ../../mrapps/jobcount.so & maybe_quiet $TIMEOUT ../mrworker ../../mrapps/jobcount.so NT=`cat mr-out* | awk '{print $2}'` if [ "$NT" -eq "8" ] then echo '---' job count test: PASS else echo '---' map jobs ran incorrect number of times "($NT != 8)" echo '---' job count test: FAIL failed_any=1 fi wait ######################################################### # test whether any worker or coordinator exits before the # task has completed (i.e., all output files have been finalized) rm -f mr-* echo '***' Starting early exit test. DF=anydone$$ rm -f $DF (maybe_quiet $TIMEOUT ../mrcoordinator ../pg*txt; touch $DF) & # give the coordinator time to create the sockets. sleep 1 # start multiple workers. (maybe_quiet $TIMEOUT ../mrworker ../../mrapps/early_exit.so; touch $DF) & (maybe_quiet $TIMEOUT ../mrworker ../../mrapps/early_exit.so; touch $DF) & (maybe_quiet $TIMEOUT ../mrworker ../../mrapps/early_exit.so; touch $DF) & # wait for any of the coord or workers to exit. # `jobs` ensures that any completed old processes from other tests # are not waited upon. jobs &> /dev/null if [[ "$OSTYPE" = "darwin"* ]] then # bash on the Mac doesn't have wait -n while [ ! -e $DF ] do sleep 0.2 done else # the -n causes wait to wait for just one child process, # rather than waiting for all to finish. wait -n fi rm -f $DF # a process has exited. this means that the output should be finalized # otherwise, either a worker or the coordinator exited early sort mr-out* | grep . > mr-wc-all-initial # wait for remaining workers and coordinator to exit. wait # compare initial and final outputs sort mr-out* | grep . > mr-wc-all-final if cmp mr-wc-all-final mr-wc-all-initial then echo '---' early exit test: PASS else echo '---' output changed after first worker exited echo '---' early exit test: FAIL failed_any=1 fi rm -f mr-* ######################################################### echo '***' Starting crash test. # generate the correct output ../mrsequential ../../mrapps/nocrash.so ../pg*txt || exit 1 sort mr-out-0 > mr-correct-crash.txt rm -f mr-out* rm -f mr-done ((maybe_quiet $TIMEOUT2 ../mrcoordinator ../pg*txt); touch mr-done ) & sleep 1 # start multiple workers maybe_quiet $TIMEOUT2 ../mrworker ../../mrapps/crash.so & # mimic rpc.go's coordinatorSock() SOCKNAME=/var/tmp/5840-mr-`id -u` ( while [ -e $SOCKNAME -a ! -f mr-done ] do maybe_quiet $TIMEOUT2 ../mrworker ../../mrapps/crash.so sleep 1 done ) & ( while [ -e $SOCKNAME -a ! -f mr-done ] do maybe_quiet $TIMEOUT2 ../mrworker ../../mrapps/crash.so sleep 1 done ) & while [ -e $SOCKNAME -a ! -f mr-done ] do maybe_quiet $TIMEOUT2 ../mrworker ../../mrapps/crash.so sleep 1 done wait rm $SOCKNAME sort mr-out* | grep . > mr-crash-all if cmp mr-crash-all mr-correct-crash.txt then echo '---' crash test: PASS else echo '---' crash output is not the same as mr-correct-crash.txt echo '---' crash test: FAIL failed_any=1 fi ######################################################### if [ $failed_any -eq 0 ]; then echo '***' PASSED ALL TESTS else echo '***' FAILED SOME TESTS exit 1 fi