bash_test.sh (9312B)
1 #!/bin/bash 2 # Copyright (c) the JPEG XL Project Authors. All rights reserved. 3 # 4 # Use of this source code is governed by a BSD-style 5 # license that can be found in the LICENSE file. 6 7 # Tests implemented in bash. These typically will run checks about the source 8 # code rather than the compiled one. 9 10 MYDIR=$(dirname $(realpath "$0")) 11 12 set -u 13 14 test_includes() { 15 local ret=0 16 local f 17 for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do 18 if [ ! -e "$f" ]; then 19 continue 20 fi 21 # Check that the full paths to the public headers are not used, since users 22 # of the library will include the library as: #include "jxl/foobar.h". 23 if grep -i -H -n -E '#include\s*[<"]lib/include/jxl' "$f" >&2; then 24 echo "Don't add \"include/\" to the include path of public headers." >&2 25 ret=1 26 fi 27 28 if [[ "${f#third_party/}" == "$f" ]]; then 29 # $f is not in third_party/ 30 31 # Check that local files don't use the full path to third_party/ 32 # directory since the installed versions will not have that path. 33 # Add an exception for third_party/dirent.h. 34 if grep -v -F 'third_party/dirent.h' "$f" | \ 35 grep -i -H -n -E '#include\s*[<"]third_party/' >&2 && 36 [[ $ret -eq 0 ]]; then 37 cat >&2 <<EOF 38 $f: Don't add third_party/ to the include path of third_party projects. This \ 39 makes it harder to use installed system libraries instead of the third_party/ \ 40 ones. 41 EOF 42 ret=1 43 fi 44 fi 45 46 done 47 return ${ret} 48 } 49 50 test_include_collision() { 51 local ret=0 52 local f 53 for f in $(git ls-files | grep -E '^lib/include/'); do 54 if [ ! -e "$f" ]; then 55 continue 56 fi 57 local base=${f#lib/include/} 58 if [[ -e "lib/${base}" ]]; then 59 echo "$f: Name collision, both $f and lib/${base} exist." >&2 60 ret=1 61 fi 62 done 63 return ${ret} 64 } 65 66 test_copyright() { 67 local ret=0 68 local f 69 for f in $( 70 git ls-files | grep -E \ 71 '(Dockerfile.*|\.c|\.cc|\.cpp|\.gni|\.h|\.java|\.sh|\.m|\.py|\.ui|\.yml)$'); do 72 if [ ! -e "$f" ]; then 73 continue 74 fi 75 if [[ "${f#third_party/}" == "$f" ]]; then 76 # $f is not in third_party/ 77 if ! head -n 10 "$f" | 78 grep -F 'Copyright (c) the JPEG XL Project Authors.' >/dev/null ; then 79 echo "$f: Missing Copyright blob near the top of the file." >&2 80 ret=1 81 fi 82 if ! head -n 10 "$f" | 83 grep -F 'Use of this source code is governed by a BSD-style' \ 84 >/dev/null ; then 85 echo "$f: Missing License blob near the top of the file." >&2 86 ret=1 87 fi 88 fi 89 done 90 return ${ret} 91 } 92 93 # Check that we don't use "%zu" or "%zd" in format string for size_t. 94 test_printf_size_t() { 95 local ret=0 96 if grep -n -E '%[0-9]*z[udx]' \ 97 $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$'); then 98 echo "Don't use '%zu' or '%zd' in a format string, instead use " \ 99 "'%\" PRIuS \"' or '%\" PRIdS \"'." >&2 100 ret=1 101 fi 102 103 if grep -n -E '[^_]gtest\.h' \ 104 $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then 105 echo "Don't include gtest directly, instead include 'testing.h'. " >&2 106 ret=1 107 fi 108 109 if grep -n -E 'gmock\.h' \ 110 $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then 111 echo "Don't include gmock directly, instead include 'testing.h'. " >&2 112 ret=1 113 fi 114 115 local f 116 for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' | 117 cut -f 1 -d : | uniq); do 118 if [ ! -e "$f" ]; then 119 continue 120 fi 121 if ! grep -F printf_macros.h "$f" >/dev/null; then 122 echo "$f: Add lib/jxl/base/printf_macros.h for PRI.S, or use other " \ 123 "types for code outside lib/jxl library." >&2 124 ret=1 125 fi 126 done 127 128 for f in $(git ls-files | grep -E "\.h$" | grep -v -E '(printf_macros\.h|testing\.h)' | 129 xargs grep -n 'PRI[udx]S'); do 130 # Having PRIuS / PRIdS in a header file means that printf_macros.h may 131 # be included before a system header, in particular before gtest headers. 132 # those may re-define PRIuS unconditionally causing a compile error. 133 echo "$f: Don't use PRI.S in header files. Sorry." 134 ret=1 135 done 136 137 return ${ret} 138 } 139 140 # Check that "dec_" code doesn't depend on "enc_" headers. 141 test_dec_enc_deps() { 142 local ret=0 143 local f 144 for f in $(git ls-files | grep -E '/dec_'); do 145 if [ ! -e "$f" ]; then 146 continue 147 fi 148 if [[ "${f#third_party/}" == "$f" ]]; then 149 # $f is not in third_party/ 150 if grep -n -H -E "#include.*/enc_" "$f" >&2; then 151 echo "$f: Don't include \"enc_*\" files from \"dec_*\" files." >&2 152 ret=1 153 fi 154 fi 155 done 156 return ${ret} 157 } 158 159 # Check for git merge conflict markers. 160 test_merge_conflict() { 161 local ret=0 162 TEXT_FILES='(\.cc|\.cpp|\.h|\.sh|\.m|\.py|\.md|\.txt|\.cmake)$' 163 for f in $(git ls-files | grep -E "${TEXT_FILES}"); do 164 if [ ! -e "$f" ]; then 165 continue 166 fi 167 if grep -E '^<<<<<<< ' "$f"; then 168 echo "$f: Found git merge conflict marker. Please resolve." >&2 169 ret=1 170 fi 171 done 172 return ${ret} 173 } 174 175 # Check that the library and the package have the same version. This prevents 176 # accidentally having them out of sync. 177 get_version() { 178 local varname=$1 179 local line=$(grep -F "set(${varname} " lib/CMakeLists.txt | head -n 1) 180 [[ -n "${line}" ]] 181 line="${line#set(${varname} }" 182 line="${line%)}" 183 echo "${line}" 184 } 185 186 test_version() { 187 local major=$(get_version JPEGXL_MAJOR_VERSION) 188 local minor=$(get_version JPEGXL_MINOR_VERSION) 189 local patch=$(get_version JPEGXL_PATCH_VERSION) 190 # Check that the version is not empty 191 if [[ -z "${major}${minor}${patch}" ]]; then 192 echo "Couldn't parse version from CMakeLists.txt" >&2 193 return 1 194 fi 195 local pkg_version=$(head -n 1 debian/changelog) 196 # Get only the part between the first "jpeg-xl (" and the following ")". 197 pkg_version="${pkg_version#jpeg-xl (}" 198 pkg_version="${pkg_version%%)*}" 199 if [[ -z "${pkg_version}" ]]; then 200 echo "Couldn't parse version from debian package" >&2 201 return 1 202 fi 203 204 local lib_version="${major}.${minor}.${patch}" 205 lib_version="${lib_version%.0}" 206 if [[ "${pkg_version}" != "${lib_version}"* ]]; then 207 echo "Debian package version (${pkg_version}) doesn't match library" \ 208 "version (${lib_version})." >&2 209 return 1 210 fi 211 return 0 212 } 213 214 # Check that the SHA versions in deps.sh matches the git submodules. 215 test_deps_version() { 216 while IFS= read -r line; do 217 if [[ "${line:0:10}" != "[submodule" ]]; then 218 continue 219 fi 220 line="${line#[submodule \"}" 221 line="${line%\"]}" 222 local varname=$(tr '[:lower:]' '[:upper:]' <<< "${line}") 223 varname="${varname/\//_}" 224 if ! grep -F "${varname}=" deps.sh >/dev/null; then 225 # Ignoring submodule not in deps.sh 226 continue 227 fi 228 local deps_sha=$(grep -F "${varname}=" deps.sh | cut -f 2 -d '"') 229 [[ -n "${deps_sha}" ]] 230 local git_sha=$(git ls-tree -r HEAD "${line}" | cut -f 1 | cut -f 3 -d ' ') 231 if [[ "${deps_sha}" != "${git_sha}" ]]; then 232 cat >&2 <<EOF 233 deps.sh: SHA for project ${line} is at ${deps_sha} but the git submodule is at 234 ${git_sha}. Please update deps.sh 235 236 If you did not intend to change the submodule's SHA value, it is possible that 237 you accidentally included this change in your commit after a rebase or checkout 238 without running "git submodule --init". To revert the submodule change run from 239 the top checkout directory: 240 241 git -C ${line} checkout ${deps_sha} 242 git commit --amend ${line} 243 244 EOF 245 return 1 246 fi 247 done < .gitmodules 248 } 249 250 # Make sure that all the Fields objects are fuzzed directly. 251 test_fuzz_fields() { 252 local ret=0 253 # List all the classes of the form "ClassName : public Fields". 254 # This doesn't catch class names that are too long to fit. 255 local field_classes=$( git ls-files | 256 grep -E '\.(cc|h)' | grep -v 'test\.cc$' | 257 xargs grep -h -o -E '\b[^ ]+ : public Fields' | cut -f 1 -d ' ') 258 local classname 259 for classname in ${field_classes}; do 260 if [ ! -e "$classname" ]; then 261 continue 262 fi 263 if ! grep -E "\\b${classname}\\b" tools/fields_fuzzer.cc >/dev/null; then 264 cat >&2 <<EOF 265 tools/fields_fuzzer.cc: Class ${classname} not found in the fields_fuzzer. 266 EOF 267 ret=1 268 fi 269 done 270 return $ret 271 } 272 273 # Test that we don't use %n in C++ code to avoid using it in printf and scanf. 274 # This test is not very precise but in cases where "module n" is needed we would 275 # normally have "% n" instead of "%n". Using %n is not allowed in Android 10+. 276 test_percent_n() { 277 local ret=0 278 local f 279 for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do 280 if [ ! -e "$f" ]; then 281 continue 282 fi 283 if grep -i -H -n -E '%h*n' "$f" >&2; then 284 echo "Don't use \"%n\"." >&2 285 ret=1 286 fi 287 done 288 return ${ret} 289 } 290 291 main() { 292 local ret=0 293 cd "${MYDIR}" 294 295 if ! git rev-parse >/dev/null 2>/dev/null; then 296 echo "Not a git checkout, skipping bash_test" 297 return 0 298 fi 299 300 IFS=$'\n' 301 for f in $(declare -F); do 302 local test_name=$(echo "$f" | cut -f 3 -d ' ') 303 # Runs all the local bash functions that start with "test_". 304 if [[ "${test_name}" == test_* ]]; then 305 echo "Test ${test_name}: Start" 306 if ${test_name}; then 307 echo "Test ${test_name}: PASS" 308 else 309 echo "Test ${test_name}: FAIL" 310 ret=1 311 fi 312 fi 313 done 314 return ${ret} 315 } 316 317 main "$@"