visual_regression.sh 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #!/bin/bash
  2. # This script runs a visual regression test on all the images
  3. # generated from OSMD samples (npm run generate:current and npm run generate:blessed)
  4. #
  5. # inspired by and adapted from Vexflow's visual regression tests.
  6. #
  7. # Prerequisites: ImageMagick
  8. #
  9. # On OSX: $ brew install imagemagick
  10. # On Linux: $ apt-get install imagemagick
  11. #
  12. # Usage:
  13. #
  14. #
  15. # First generate the known good or previous state PNG images you want to compare to, e.g. the develop branch or last release:
  16. #
  17. # npm run generate:blessed
  18. #
  19. # Make changes in OSMD, then generate your new images:
  20. #
  21. # npm run generate:current
  22. #
  23. # Run the regression tests against the blessed images in visual_regression/blessed.
  24. #
  25. # npm run test:visual
  26. # # npm will navigate to the base folder automatically
  27. #
  28. # # or: (this should be done from the main OSMD folder)
  29. # # sh test/Util/visual_regression.sh [imageBaseFolder] [sampleShellPrefix]
  30. # # example: sh test/Util/visual_regression.sh ./visual_regression OSMD_function_test_
  31. # # will run visual regression tests for all images matching OSMD_function_test_*.png.
  32. #
  33. # Check visual_regression/diff/results.txt for results. This file is sorted
  34. # by PHASH difference (most different files on top.) The composite diff
  35. # images for failed tests (i.e., PHASH > 1.0) are stored in visual_regression/diff.
  36. #
  37. # (If you are satisfied with the differences, copy *.png from visual_regression/current
  38. # into visual_regression/blessed, and submit your change (TODO))
  39. # PNG viewer on OSX. Switch this to whatever your system uses.
  40. # VIEWER=open
  41. # Show images over this PHASH threshold.
  42. # 0.01 is probably too low, but a good first pass.
  43. # 0.0001 catches for example a repetition ending not having a down line at the end (see Saltarello bar 10) (0.001 doesn't catch this)
  44. THRESHOLD=0.0001
  45. # Set up Directories
  46. # It does not matter where this script is executed, as long as these folders are given correctly (and blessed/current have png images set up correctly)
  47. BUILDFOLDER=./visual_regression
  48. if [ "$1" != "" ]
  49. then
  50. BUILDFOLDER=$1
  51. fi
  52. BLESSED=$BUILDFOLDER/blessed
  53. CURRENT=$BUILDFOLDER/current
  54. DIFF=$BUILDFOLDER/diff
  55. # diff also acts as the temp folder here, unlike in Vexflow, where it is current.
  56. # it would be nice to have a tmp folder (for temporary files), but we'd want to delete the folder entirely, and we'd better not risk using rm -rf in a script
  57. # All results are stored here.
  58. RESULTS=$DIFF/results.txt
  59. WARNINGS=$DIFF/warnings.txt
  60. # If no prefix is provided, test all images.
  61. if [ "$2" == "" ]
  62. then
  63. files=*.png
  64. else
  65. files=$2*.png
  66. echo "image filter (shell): $files"
  67. fi
  68. ## Sanity checks: some simple checks that the script can run correctly (doesn't validate pngs)
  69. folderWarningStringMsg="Exiting without running visual regression tests."
  70. totalCurrentImages=`ls -1 $CURRENT/$files | wc -l | xargs` # xargs trims space
  71. if [ $? -ne 0 ] || [ "$totalCurrentImages" -lt 1 ] # $? returns the exit code of the previous command (ls). (0 is success)
  72. then
  73. echo Missing images in $CURRENT.
  74. echo Please run \"npm run generate:current\"
  75. exit 1
  76. fi
  77. totalBlessedImages=`ls -1 $BLESSED/$files | wc -l | xargs`
  78. if [ $? -ne 0 ] || [ "$totalBlessedImages" -lt 1 ]
  79. then
  80. echo Missing images in $BLESSED.
  81. echo Please run \"npm run generate:blessed\"
  82. exit 1
  83. fi
  84. # check that #currentImages == #blessedImages (will continue anyways)
  85. if [ ! "$totalCurrentImages" -eq "$totalBlessedImages" ]
  86. then
  87. echo "Warning: Number of current images ($totalCurrentImages) is not the same as blessed images ($totalBlessedImages). Continuing anyways.\n"
  88. else
  89. echo "Found $totalCurrentImages current and $totalBlessedImages blessed png files (not tested if valid). Continuing.\n"
  90. fi
  91. # ----------------- end of sanity checks -----------------
  92. mkdir -p $DIFF
  93. if [ -e "$RESULTS" ]
  94. then
  95. rm $DIFF/*
  96. fi
  97. touch $RESULTS
  98. touch $RESULTS.fails
  99. # this shouldn't be named .fail because we have a *.fail shell match further below, which will loop endlessly if files are in the same folder (diff).
  100. touch $WARNINGS
  101. # Number of simultaneous jobs
  102. nproc=$(sysctl -n hw.physicalcpu 2> /dev/null || nproc)
  103. if [ -n "$NPROC" ]; then
  104. nproc=$NPROC
  105. fi
  106. total=`ls -l $BLESSED/$files | wc -l | sed 's/[[:space:]]//g'`
  107. echo "Running $total tests with threshold $THRESHOLD (nproc=$nproc)..."
  108. function ProgressBar {
  109. let _progress=(${1}*100/${2}*100)/100
  110. let _done=(${_progress}*4)/10
  111. let _left=40-$_done
  112. _fill=$(printf "%${_done}s")
  113. _empty=$(printf "%${_left}s")
  114. printf "\rProgress : [${_fill// /#}${_empty// /-}] ${_progress}%%"
  115. }
  116. function diff_image() {
  117. local image=$1
  118. local name=`basename $image .png`
  119. local blessed=$BLESSED/$name.png
  120. local current=$CURRENT/$name.png
  121. local diff=$DIFF/$name.png-temp
  122. if [ ! -e "$current" ]
  123. then
  124. echo " Warning: $name.png missing in $CURRENT. Skipped." >$diff.warn
  125. #((total--))
  126. return
  127. fi
  128. if [ ! -e "$blessed" ]
  129. then
  130. echo " Warning: $name.png doesn't exist in $BLESSED. Skipped." >$diff.warn
  131. #((total--))
  132. return
  133. fi
  134. cp $blessed $diff-a.png
  135. cp $current $diff-b.png
  136. # Calculate the difference metric and store the composite diff image.
  137. local hash=`compare -metric PHASH -highlight-color '#ff000050' $diff-b.png $diff-a.png $diff-diff.png 2>&1`
  138. local isGT=`echo "$hash > $THRESHOLD" | bc -l`
  139. if [ "$isGT" == "1" ]
  140. then
  141. # Add the result to results.txt
  142. echo $name $hash >$diff.fail
  143. # Threshold exceeded, save the diff and the original, current
  144. cp $diff-diff.png $DIFF/$name.png
  145. cp $diff-a.png $DIFF/$name'_'Blessed.png
  146. cp $diff-b.png $DIFF/$name'_'Current.png
  147. echo
  148. echo "Test: $name"
  149. echo " PHASH value exceeds threshold: $hash > $THRESHOLD"
  150. echo " Image diff stored in $DIFF/$name.png"
  151. # $VIEWER "$diff-diff.png" "$diff-a.png" "$diff-b.png"
  152. # echo 'Hit return to process next image...'
  153. # read
  154. else
  155. echo $name $hash >$diff.pass
  156. fi
  157. rm -f $diff-a.png $diff-b.png $diff-diff.png
  158. }
  159. function wait_jobs () {
  160. local n=$1
  161. while [[ "$(jobs -r | wc -l)" -ge "$n" ]] ; do
  162. # echo ===================================== && jobs -lr
  163. # wait the oldest job.
  164. local pid_to_wait=`jobs -rp | head -1`
  165. # echo wait $pid_to_wait
  166. wait $pid_to_wait &> /dev/null
  167. done
  168. }
  169. count=0
  170. for image in $CURRENT/$files
  171. do
  172. count=$((count + 1))
  173. ProgressBar ${count} ${total}
  174. wait_jobs $nproc
  175. diff_image $image &
  176. done
  177. wait
  178. cat $DIFF/*.warn 1>$WARNINGS 2>/dev/null
  179. rm -f $DIFF/*.warn
  180. ## Check for files newly built that are not yet blessed.
  181. for image in $CURRENT/$files
  182. do
  183. name=`basename $image .png`
  184. blessed=$BLESSED/$name.png
  185. current=$CURRENT/$name.png
  186. done
  187. num_warnings=`cat $WARNINGS | wc -l`
  188. cat $DIFF/*.fail 1>$RESULTS.fails 2>/dev/null
  189. num_fails=`cat $RESULTS.fails | wc -l`
  190. rm -f $DIFF/*.fail
  191. # Sort results by PHASH
  192. sort -r -n -k 2 $RESULTS.fails >$RESULTS
  193. sort -r -n -k 2 $DIFF/*.pass 1>>$RESULTS 2>/dev/null
  194. rm -f $DIFF/*.pass $RESULTS.fails
  195. echo
  196. echo Results stored in $DIFF/results.txt
  197. echo All images with a difference over threshold, $THRESHOLD, are
  198. echo available in $DIFF, sorted by perceptual hash.
  199. echo
  200. if [ "$num_warnings" -gt 0 ]
  201. then
  202. echo
  203. echo "You have $num_warnings warning(s):"
  204. cat $WARNINGS
  205. fi
  206. if [ "$num_fails" -gt 0 ]
  207. then
  208. echo "You have $num_fails fail(s):"
  209. head -n $num_fails $RESULTS
  210. else
  211. echo "Success - All diffs under threshold!"
  212. fi