From 8631c1b8a21f2bc0c5a814d125a2e2eea2d2bf86 Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Tue, 22 May 2012 10:37:20 -0700 Subject: [PATCH 1/5] configure: initial support for CXX, CXXFLAGS variables Use CXX rather than assuming g++ to invoke the compiler. Also introduce separate CXXFLAGS, as certain CFLAGS we enable by default cause warnings with g++. Change-Id: Ia2f40ef27c93e45c971d070cc58bdcde9da2ac7c --- build/make/Makefile | 7 ++++--- build/make/configure.sh | 45 ++++++++++++++++++++++++++++++++++++++++- libs.mk | 4 ++-- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/build/make/Makefile b/build/make/Makefile index b6cf3208a..26ca0be70 100644 --- a/build/make/Makefile +++ b/build/make/Makefile @@ -66,6 +66,7 @@ endif BUILD_ROOT?=. VPATH=$(SRC_PATH_BARE) CFLAGS+=-I$(BUILD_PFX)$(BUILD_ROOT) -I$(SRC_PATH) +CXXFLAGS+=-I$(BUILD_PFX)$(BUILD_ROOT) -I$(SRC_PATH) ASFLAGS+=-I$(BUILD_PFX)$(BUILD_ROOT)/ -I$(SRC_PATH)/ DIST_DIR?=dist HOSTCC?=gcc @@ -111,11 +112,11 @@ $(BUILD_PFX)%.c.o: %.c $(BUILD_PFX)%.cc.d: %.cc $(if $(quiet),@echo " [DEP] $@") $(qexec)mkdir -p $(dir $@) - $(qexec)g++ $(INTERNAL_CFLAGS) $(CFLAGS) -M $< | $(fmt_deps) > $@ + $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -M $< | $(fmt_deps) > $@ $(BUILD_PFX)%.cc.o: %.cc $(if $(quiet),@echo " [CXX] $@") - $(qexec)g++ $(INTERNAL_CFLAGS) $(CFLAGS) -c -o $@ $< + $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -c -o $@ $< $(BUILD_PFX)%.asm.d: %.asm $(if $(quiet),@echo " [DEP] $@") @@ -213,7 +214,7 @@ define linkerxx_template $(1): $(filter-out -%,$(2)) $(1): $(if $(quiet),@echo " [LD] $$@") - $(qexec)g++ $$(strip $$(INTERNAL_LDFLAGS) $$(LDFLAGS) -o $$@ $(2) $(3) $$(extralibs)) + $(qexec)$$(CXX) $$(strip $$(INTERNAL_LDFLAGS) $$(LDFLAGS) -o $$@ $(2) $(3) $$(extralibs)) endef # make-3.80 has a bug with expanding large input strings to the eval function, # which was triggered in some cases by the following component of diff --git a/build/make/configure.sh b/build/make/configure.sh index 3118c0a2d..11ebdf015 100755 --- a/build/make/configure.sh +++ b/build/make/configure.sh @@ -166,6 +166,17 @@ is_in(){ add_cflags() { CFLAGS="${CFLAGS} $@" + CXXFLAGS="${CXXFLAGS} $@" +} + + +add_cflags_only() { + CFLAGS="${CFLAGS} $@" +} + + +add_cxxflags_only() { + CXXFLAGS="${CXXFLAGS} $@" } @@ -277,6 +288,13 @@ check_cc() { check_cmd ${CC} ${CFLAGS} "$@" -c -o ${TMP_O} ${TMP_C} } +check_cxx() { + log check_cxx "$@" + cat >${TMP_C} + log_file ${TMP_C} + check_cmd ${CXX} ${CXXFLAGS} "$@" -c -o ${TMP_O} ${TMP_C} +} + check_cpp() { log check_cpp "$@" cat > ${TMP_C} @@ -310,8 +328,25 @@ int x; EOF } +check_cxxflags() { + log check_cxxflags "$@" + + # Catch CFLAGS that trigger CXX warnings + case "$CXX" in + *g++*) check_cxx -Werror "$@" <> $1 << EOF @@ -379,6 +416,7 @@ TOOLCHAIN=${toolchain} ASM_CONVERSION=${asm_conversion_cmd:-${source_path}/build/make/ads2gas.pl} CC=${CC} +CXX=${CXX} AR=${AR} LD=${LD} AS=${AS} @@ -386,6 +424,7 @@ STRIP=${STRIP} NM=${NM} CFLAGS = ${CFLAGS} +CXXFLAGS = ${CXXFLAGS} ARFLAGS = -rus\$(if \$(quiet),c,v) LDFLAGS = ${LDFLAGS} ASFLAGS = ${ASFLAGS} @@ -538,6 +577,7 @@ post_process_cmdline() { setup_gnu_toolchain() { CC=${CC:-${CROSS}gcc} + CXX=${CXX:-${CROSS}g++} AR=${AR:-${CROSS}ar} LD=${LD:-${CROSS}${link_with_cc:-ld}} AS=${AS:-${CROSS}as} @@ -792,6 +832,7 @@ process_common_toolchain() { -name "arm-linux-androideabi-gcc*" -print -quit` TOOLCHAIN_PATH=${COMPILER_LOCATION%/*}/arm-linux-androideabi- CC=${TOOLCHAIN_PATH}gcc + CXX=${TOOLCHAIN_PATH}g++ AR=${TOOLCHAIN_PATH}ar LD=${TOOLCHAIN_PATH}gcc AS=${TOOLCHAIN_PATH}as @@ -827,6 +868,7 @@ process_common_toolchain() { SDK_PATH=${sdk_path} fi TOOLCHAIN_PATH=${SDK_PATH}/usr/bin + CXX=${TOOLCHAIN_PATH}/g++ CC=${TOOLCHAIN_PATH}/gcc AR=${TOOLCHAIN_PATH}/ar LD=${TOOLCHAIN_PATH}/arm-apple-darwin10-llvm-gcc-4.2 @@ -938,6 +980,7 @@ process_common_toolchain() { ;; solaris*) CC=${CC:-${CROSS}gcc} + CXX=${CXX:-${CROSS}g++} LD=${LD:-${CROSS}gcc} CROSS=${CROSS:-g} ;; diff --git a/libs.mk b/libs.mk index f241d07ac..f25664932 100644 --- a/libs.mk +++ b/libs.mk @@ -397,8 +397,8 @@ else include $(SRC_PATH_BARE)/third_party/googletest/gtest.mk GTEST_SRCS := $(addprefix third_party/googletest/src/,$(call enabled,GTEST_SRCS)) GTEST_OBJS=$(call objs,$(GTEST_SRCS)) -$(GTEST_OBJS) $(GTEST_OBJS:.o=.d): CFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src -$(GTEST_OBJS) $(GTEST_OBJS:.o=.d): CFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src/include +$(GTEST_OBJS) $(GTEST_OBJS:.o=.d): CXXFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src +$(GTEST_OBJS) $(GTEST_OBJS:.o=.d): CXXFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src/include OBJS-$(BUILD_LIBVPX) += $(GTEST_OBJS) LIBS-$(BUILD_LIBVPX) += $(BUILD_PFX)libgtest.a $(BUILD_PFX)libgtest_g.a $(BUILD_PFX)libgtest_g.a: $(GTEST_OBJS) From e82d261d10729b08ebe3e75fd6927d9993fc9182 Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Wed, 16 May 2012 16:25:51 -0700 Subject: [PATCH 2/5] Build unit tests monolithically Rework unit tests to have a single executable rather than many, which should avoid pollution of the visual studio project namespace, improve build times, and make it easier to use the gtest test sharding system when we get these going on the continuous build cluster. Change-Id: If4c3e5d4b3515522869de6c89455c2a64697cca6 --- libs.mk | 28 ++-- test/test.mk | 2 + .../test_libvpx.cc | 21 +-- vp8/common/idctllm_test.cc | 120 ++++++++++++++++-- vp8/common/idctllm_test.h | 113 ----------------- vp8/encoder/boolcoder_test.cc | 6 - vp8/vp8_common.mk | 3 +- vp8/vp8cx.mk | 2 +- 8 files changed, 130 insertions(+), 165 deletions(-) create mode 100644 test/test.mk rename vp8/common/x86/idctllm_mmx_test.cc => test/test_libvpx.cc (50%) mode change 100755 => 100644 delete mode 100755 vp8/common/idctllm_test.h diff --git a/libs.mk b/libs.mk index f25664932..516e6d834 100644 --- a/libs.mk +++ b/libs.mk @@ -329,7 +329,6 @@ CLEAN-OBJS += $(BUILD_PFX)vpx_version.h # # Rule to generate runtime cpu detection files # -$(OBJS-yes:.o=.d): $(BUILD_PFX)vpx_rtcd.h $(BUILD_PFX)vpx_rtcd.h: $(SRC_PATH_BARE)/$(sort $(filter %rtcd_defs.sh,$(CODEC_SRCS))) @echo " [CREATE] $@" $(qexec)$(SRC_PATH_BARE)/build/make/rtcd.sh --arch=$(TGT_ISA) \ @@ -346,14 +345,15 @@ CODEC_DOC_SRCS += vpx/vpx_codec.h \ ## ## libvpx test directives ## - ifeq ($(CONFIG_UNIT_TESTS),yes) + +include $(SRC_PATH_BARE)/test/test.mk +LIBVPX_TEST_SRCS=$(addprefix test/,$(call enabled,LIBVPX_TEST_SRCS)) +LIBVPX_TEST_BINS=./test_libvpx + ifeq ($(CONFIG_EXTERNAL_BUILD),yes) ifeq ($(CONFIG_MSVS),yes) -LIBVPX_TEST_SRCS=$(filter %_test.cc,$(call enabled,CODEC_SRCS)) -LIBVPX_TEST_BINS=$(sort $(LIBVPX_TEST_SRCS:.cc.o=)) - gtest.vcproj: $(SRC_PATH_BARE)/third_party/googletest/src/src/gtest-all.cc @echo " [CREATE] $@" $(SRC_PATH_BARE)/build/make/gen_msvs_proj.sh \ @@ -403,17 +403,20 @@ OBJS-$(BUILD_LIBVPX) += $(GTEST_OBJS) LIBS-$(BUILD_LIBVPX) += $(BUILD_PFX)libgtest.a $(BUILD_PFX)libgtest_g.a $(BUILD_PFX)libgtest_g.a: $(GTEST_OBJS) -LIBVPX_TEST_SRCS=$(filter %_test.cc,$(call enabled,CODEC_SRCS)) -LIBVPX_TEST_OBJS=$(call objs,$(LIBVPX_TEST_SRCS)) -$(LIBVPX_TEST_OBJS) $(LIBVPX_TEST_OBJS:.o=.d): CFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src -$(LIBVPX_TEST_OBJS) $(LIBVPX_TEST_OBJS:.o=.d): CFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src/include -LIBVPX_TEST_BINS=$(sort $(LIBVPX_TEST_OBJS:.cc.o=)) +LIBVPX_TEST_OBJS=$(sort $(call objs,$(LIBVPX_TEST_SRCS))) +$(LIBVPX_TEST_OBJS) $(LIBVPX_TEST_OBJS:.o=.d): CXXFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src +$(LIBVPX_TEST_OBJS) $(LIBVPX_TEST_OBJS:.o=.d): CXXFLAGS += -I$(SRC_PATH_BARE)/third_party/googletest/src/include OBJS-$(BUILD_LIBVPX) += $(LIBVPX_TEST_OBJS) +# Install test sources only if codec source is included +INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(patsubst $(SRC_PATH_BARE)/%,%,\ + $(shell find $(SRC_PATH_BARE)/third_party/googletest -type f)) +INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(LIBVPX_TEST_SRCS) + $(foreach bin,$(LIBVPX_TEST_BINS),\ $(if $(BUILD_LIBVPX),$(eval $(bin): libvpx.a libgtest.a ))\ $(if $(BUILD_LIBVPX),$(eval $(call linkerxx_template,$(bin),\ - $(bin).cc.o \ + $(LIBVPX_TEST_OBJS) \ -L. -lvpx -lgtest -lpthread -lm)\ )))\ $(if $(LIPO_LIBS),$(eval $(call lipo_bin_template,$(bin))))\ @@ -436,3 +439,6 @@ libs.doxy: $(CODEC_DOC_SRCS) @echo "PREDEFINED = VPX_CODEC_DISABLE_COMPAT" >> $@ @echo "INCLUDE_PATH += ." >> $@; @echo "ENABLED_SECTIONS += $(sort $(CODEC_DOC_SECTIONS))" >> $@ + +## Generate vpx_rtcd.h for all objects +$(OBJS-yes:.o=.d): $(BUILD_PFX)vpx_rtcd.h diff --git a/test/test.mk b/test/test.mk new file mode 100644 index 000000000..e486795e3 --- /dev/null +++ b/test/test.mk @@ -0,0 +1,2 @@ +LIBVPX_TEST_SRCS-yes += test.mk +LIBVPX_TEST_SRCS-yes += test_libvpx.cc diff --git a/vp8/common/x86/idctllm_mmx_test.cc b/test/test_libvpx.cc old mode 100755 new mode 100644 similarity index 50% rename from vp8/common/x86/idctllm_mmx_test.cc rename to test/test_libvpx.cc index 8c115335e..48bfc6c76 --- a/vp8/common/x86/idctllm_mmx_test.cc +++ b/test/test_libvpx.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source @@ -7,24 +7,7 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ - - - extern "C" { - void vp8_short_idct4x4llm_mmx(short *input, unsigned char *pred_ptr, - int pred_stride, unsigned char *dst_ptr, - int dst_stride); -} - -#include "vp8/common/idctllm_test.h" - -namespace -{ - -INSTANTIATE_TEST_CASE_P(MMX, IDCTTest, - ::testing::Values(vp8_short_idct4x4llm_mmx)); - -} // namespace - +#include "third_party/googletest/src/include/gtest/gtest.h" int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/vp8/common/idctllm_test.cc b/vp8/common/idctllm_test.cc index 0f6ebe7fe..dd42e2299 100755 --- a/vp8/common/idctllm_test.cc +++ b/vp8/common/idctllm_test.cc @@ -9,23 +9,117 @@ */ - extern "C" { - void vp8_short_idct4x4llm_c(short *input, unsigned char *pred_ptr, - int pred_stride, unsigned char *dst_ptr, - int dst_stride); +extern "C" { +#include "vpx_config.h" +#include "vpx_rtcd.h" +} +#include "third_party/googletest/src/include/gtest/gtest.h" + +typedef void (*idct_fn_t)(short *input, unsigned char *pred_ptr, + int pred_stride, unsigned char *dst_ptr, + int dst_stride); +namespace { +class IDCTTest : public ::testing::TestWithParam +{ + protected: + virtual void SetUp() + { + int i; + + UUT = GetParam(); + memset(input, 0, sizeof(input)); + /* Set up guard blocks */ + for(i=0; i<256; i++) + output[i] = ((i&0xF)<4&&(i<64))?0:-1; + } + + idct_fn_t UUT; + short input[16]; + unsigned char output[256]; + unsigned char predict[256]; +}; + +TEST_P(IDCTTest, TestGuardBlocks) +{ + int i; + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(0, output[i]) << i; + else + EXPECT_EQ(255, output[i]); } -#include "vpx_config.h" -#include "idctllm_test.h" -namespace +TEST_P(IDCTTest, TestAllZeros) { + int i; + + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(0, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestAllOnes) +{ + int i; + + input[0] = 4; + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(1, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestAddOne) +{ + int i; + + for(i=0; i<256; i++) + predict[i] = i; + + input[0] = 4; + UUT(input, predict, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(i+1, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestWithData) +{ + int i; + + for(i=0; i<16; i++) + input[i] = i; + + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) > 3 || i>63) + EXPECT_EQ(255, output[i]) << "i==" << i; + else if(i == 0) + EXPECT_EQ(11, output[i]) << "i==" << i; + else if(i == 34) + EXPECT_EQ(1, output[i]) << "i==" << i; + else if(i == 2 || i == 17 || i == 32) + EXPECT_EQ(3, output[i]) << "i==" << i; + else + EXPECT_EQ(0, output[i]) << "i==" << i; +} INSTANTIATE_TEST_CASE_P(C, IDCTTest, ::testing::Values(vp8_short_idct4x4llm_c)); - -} // namespace - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); +#if HAVE_MMX +INSTANTIATE_TEST_CASE_P(MMX, IDCTTest, + ::testing::Values(vp8_short_idct4x4llm_mmx)); +#endif } diff --git a/vp8/common/idctllm_test.h b/vp8/common/idctllm_test.h deleted file mode 100755 index a6a694b18..000000000 --- a/vp8/common/idctllm_test.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2010 The WebM project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - - #include "third_party/googletest/src/include/gtest/gtest.h" -typedef void (*idct_fn_t)(short *input, unsigned char *pred_ptr, - int pred_stride, unsigned char *dst_ptr, - int dst_stride); -namespace { -class IDCTTest : public ::testing::TestWithParam -{ - protected: - virtual void SetUp() - { - int i; - - UUT = GetParam(); - memset(input, 0, sizeof(input)); - /* Set up guard blocks */ - for(i=0; i<256; i++) - output[i] = ((i&0xF)<4&&(i<64))?0:-1; - } - - idct_fn_t UUT; - short input[16]; - unsigned char output[256]; - unsigned char predict[256]; -}; - -TEST_P(IDCTTest, TestGuardBlocks) -{ - int i; - - for(i=0; i<256; i++) - if((i&0xF) < 4 && i<64) - EXPECT_EQ(0, output[i]) << i; - else - EXPECT_EQ(255, output[i]); -} - -TEST_P(IDCTTest, TestAllZeros) -{ - int i; - - UUT(input, output, 16, output, 16); - - for(i=0; i<256; i++) - if((i&0xF) < 4 && i<64) - EXPECT_EQ(0, output[i]) << "i==" << i; - else - EXPECT_EQ(255, output[i]) << "i==" << i; -} - -TEST_P(IDCTTest, TestAllOnes) -{ - int i; - - input[0] = 4; - UUT(input, output, 16, output, 16); - - for(i=0; i<256; i++) - if((i&0xF) < 4 && i<64) - EXPECT_EQ(1, output[i]) << "i==" << i; - else - EXPECT_EQ(255, output[i]) << "i==" << i; -} - -TEST_P(IDCTTest, TestAddOne) -{ - int i; - - for(i=0; i<256; i++) - predict[i] = i; - - input[0] = 4; - UUT(input, predict, 16, output, 16); - - for(i=0; i<256; i++) - if((i&0xF) < 4 && i<64) - EXPECT_EQ(i+1, output[i]) << "i==" << i; - else - EXPECT_EQ(255, output[i]) << "i==" << i; -} - -TEST_P(IDCTTest, TestWithData) -{ - int i; - - for(i=0; i<16; i++) - input[i] = i; - - UUT(input, output, 16, output, 16); - - for(i=0; i<256; i++) - if((i&0xF) > 3 || i>63) - EXPECT_EQ(255, output[i]) << "i==" << i; - else if(i == 0) - EXPECT_EQ(11, output[i]) << "i==" << i; - else if(i == 34) - EXPECT_EQ(1, output[i]) << "i==" << i; - else if(i == 2 || i == 17 || i == 32) - EXPECT_EQ(3, output[i]) << "i==" << i; - else - EXPECT_EQ(0, output[i]) << "i==" << i; -} -} diff --git a/vp8/encoder/boolcoder_test.cc b/vp8/encoder/boolcoder_test.cc index 9d9462690..00c2b7593 100644 --- a/vp8/encoder/boolcoder_test.cc +++ b/vp8/encoder/boolcoder_test.cc @@ -121,9 +121,3 @@ TEST(VP8, TestBitIO) } } } - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/vp8/vp8_common.mk b/vp8/vp8_common.mk index 3a7b146fc..0afd85f7d 100644 --- a/vp8/vp8_common.mk +++ b/vp8/vp8_common.mk @@ -30,7 +30,7 @@ VP8_COMMON_SRCS-yes += common/findnearmv.c VP8_COMMON_SRCS-yes += common/generic/systemdependent.c VP8_COMMON_SRCS-yes += common/idct_blk.c VP8_COMMON_SRCS-yes += common/idctllm.c -VP8_COMMON_SRCS-yes += common/idctllm_test.cc +LIBVPX_TEST_SRCS-yes += ../vp8/common/idctllm_test.cc VP8_COMMON_SRCS-yes += common/alloccommon.h VP8_COMMON_SRCS-yes += common/blockd.h VP8_COMMON_SRCS-yes += common/common.h @@ -85,7 +85,6 @@ VP8_COMMON_SRCS-$(CONFIG_POSTPROC) += common/postproc.c VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/dequantize_mmx.asm VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/idct_blk_mmx.c VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/idctllm_mmx.asm -VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/idctllm_mmx_test.cc VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/iwalsh_mmx.asm VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/loopfilter_mmx.asm VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/recon_mmx.asm diff --git a/vp8/vp8cx.mk b/vp8/vp8cx.mk index 78674ca5f..9f50fadfc 100644 --- a/vp8/vp8cx.mk +++ b/vp8/vp8cx.mk @@ -88,7 +88,7 @@ VP8_CX_SRCS-yes += encoder/temporal_filter.c VP8_CX_SRCS-$(CONFIG_MULTI_RES_ENCODING) += encoder/mr_dissim.c VP8_CX_SRCS-$(CONFIG_MULTI_RES_ENCODING) += encoder/mr_dissim.h -VP8_CX_SRCS-$(CONFIG_UNIT_TESTS) += encoder/boolcoder_test.cc +LIBVPX_TEST_SRCS-yes += ../vp8/encoder/boolcoder_test.cc ifeq ($(CONFIG_REALTIME_ONLY),yes) VP8_CX_SRCS_REMOVE-yes += encoder/firstpass.c From 2bf62c1dbb191861fe50d3c71e6a51372d46327e Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Tue, 22 May 2012 11:51:14 -0700 Subject: [PATCH 3/5] Enable unit tests by default Build unit tests by default if there is a working C++ toolchain available. Change-Id: I511558339b332fadfde37ef01b2dbf2755f48f89 --- configure | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/configure b/configure index 2b1328dbd..eed6f970b 100755 --- a/configure +++ b/configure @@ -586,6 +586,16 @@ process_toolchain() { if enabled postproc_visualizer; then enabled postproc || die "postproc_visualizer requires postproc to be enabled" fi + + # Enable unit tests if we have a working C++ compiler + case "$tgt_cc" in + vs*) + soft_enable unit_tests;; + *) + check_cxx "$@" < Date: Tue, 22 May 2012 11:56:31 -0700 Subject: [PATCH 4/5] Move all tests to test/ directory Consolodate the unit tests under vp8/ to the test/ directory Change-Id: I6d6a0fb60f5e3874a4d6710e9e121dd3e81a93db --- {vp8/encoder => test}/boolcoder_test.cc | 0 {vp8/common => test}/idctllm_test.cc | 0 test/test.mk | 2 ++ vp8/vp8_common.mk | 1 - vp8/vp8cx.mk | 2 -- 5 files changed, 2 insertions(+), 3 deletions(-) rename {vp8/encoder => test}/boolcoder_test.cc (100%) rename {vp8/common => test}/idctllm_test.cc (100%) diff --git a/vp8/encoder/boolcoder_test.cc b/test/boolcoder_test.cc similarity index 100% rename from vp8/encoder/boolcoder_test.cc rename to test/boolcoder_test.cc diff --git a/vp8/common/idctllm_test.cc b/test/idctllm_test.cc similarity index 100% rename from vp8/common/idctllm_test.cc rename to test/idctllm_test.cc diff --git a/test/test.mk b/test/test.mk index e486795e3..51382ee96 100644 --- a/test/test.mk +++ b/test/test.mk @@ -1,2 +1,4 @@ LIBVPX_TEST_SRCS-yes += test.mk +LIBVPX_TEST_SRCS-yes += boolcoder_test.cc +LIBVPX_TEST_SRCS-yes += idctllm_test.cc LIBVPX_TEST_SRCS-yes += test_libvpx.cc diff --git a/vp8/vp8_common.mk b/vp8/vp8_common.mk index 0afd85f7d..1fbe5d43b 100644 --- a/vp8/vp8_common.mk +++ b/vp8/vp8_common.mk @@ -30,7 +30,6 @@ VP8_COMMON_SRCS-yes += common/findnearmv.c VP8_COMMON_SRCS-yes += common/generic/systemdependent.c VP8_COMMON_SRCS-yes += common/idct_blk.c VP8_COMMON_SRCS-yes += common/idctllm.c -LIBVPX_TEST_SRCS-yes += ../vp8/common/idctllm_test.cc VP8_COMMON_SRCS-yes += common/alloccommon.h VP8_COMMON_SRCS-yes += common/blockd.h VP8_COMMON_SRCS-yes += common/common.h diff --git a/vp8/vp8cx.mk b/vp8/vp8cx.mk index 9f50fadfc..019edbd00 100644 --- a/vp8/vp8cx.mk +++ b/vp8/vp8cx.mk @@ -88,8 +88,6 @@ VP8_CX_SRCS-yes += encoder/temporal_filter.c VP8_CX_SRCS-$(CONFIG_MULTI_RES_ENCODING) += encoder/mr_dissim.c VP8_CX_SRCS-$(CONFIG_MULTI_RES_ENCODING) += encoder/mr_dissim.h -LIBVPX_TEST_SRCS-yes += ../vp8/encoder/boolcoder_test.cc - ifeq ($(CONFIG_REALTIME_ONLY),yes) VP8_CX_SRCS_REMOVE-yes += encoder/firstpass.c VP8_CX_SRCS_REMOVE-yes += encoder/temporal_filter.c From b9180fc0499a7c1480a47e17328e7a35bb788086 Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Wed, 16 May 2012 15:27:00 -0700 Subject: [PATCH 5/5] Add initial keyframe tests Implements a couple simple tests of the encoder API using the gtest framework: TestDisableKeyframes TestForceKeyframe TestKeyframeMaxDistance Change-Id: I38e93fe242fbeb30bb11b23ac12de8ddc291a28d --- test/encode_test_driver.cc | 137 ++++++++++++++++++++++++++++ test/encode_test_driver.h | 178 +++++++++++++++++++++++++++++++++++++ test/keyframe_test.cc | 103 +++++++++++++++++++++ test/test.mk | 4 + test/video_source.h | 109 +++++++++++++++++++++++ 5 files changed, 531 insertions(+) create mode 100644 test/encode_test_driver.cc create mode 100644 test/encode_test_driver.h create mode 100644 test/keyframe_test.cc create mode 100644 test/video_source.h diff --git a/test/encode_test_driver.cc b/test/encode_test_driver.cc new file mode 100644 index 000000000..0a188facf --- /dev/null +++ b/test/encode_test_driver.cc @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "test/encode_test_driver.h" +#include "test/video_source.h" +#include "third_party/googletest/src/include/gtest/gtest.h" + +namespace libvpx_test { + +void Encoder::EncodeFrame(VideoSource *video, unsigned long flags) { + if (video->img()) + EncodeFrameInternal(*video, flags); + else + Flush(); + + // Handle twopass stats + CxDataIterator iter = GetCxData(); + + while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) { + if (pkt->kind != VPX_CODEC_STATS_PKT) + continue; + + stats_->Append(*pkt); + } +} + +void Encoder::EncodeFrameInternal(const VideoSource &video, + unsigned long flags) { + vpx_codec_err_t res; + const vpx_image_t *img = video.img(); + + // Handle first frame initialization + if (!encoder_.priv) { + cfg_.g_w = img->d_w; + cfg_.g_h = img->d_h; + cfg_.g_timebase = video.timebase(); + cfg_.rc_twopass_stats_in = stats_->buf(); + res = vpx_codec_enc_init(&encoder_, &vpx_codec_vp8_cx_algo, &cfg_, 0); + ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); + } + + // Handle frame resizing + if (cfg_.g_w != img->d_w || cfg_.g_h != img->d_h) { + cfg_.g_w = img->d_w; + cfg_.g_h = img->d_h; + res = vpx_codec_enc_config_set(&encoder_, &cfg_); + ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); + } + + // Encode the frame + res = vpx_codec_encode(&encoder_, + video.img(), video.pts(), video.duration(), + flags, deadline_); + ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); +} + +void Encoder::Flush() { + const vpx_codec_err_t res = vpx_codec_encode(&encoder_, NULL, 0, 0, 0, + deadline_); + ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); +} + +void EncoderTest::SetMode(TestMode mode) { + switch (mode) { + case kRealTime: + deadline_ = VPX_DL_REALTIME; + break; + + case kOnePassGood: + case kTwoPassGood: + deadline_ = VPX_DL_GOOD_QUALITY; + break; + + case kOnePassBest: + case kTwoPassBest: + deadline_ = VPX_DL_BEST_QUALITY; + break; + + default: + ASSERT_TRUE(false) << "Unexpected mode " << mode; + } + + if (mode == kTwoPassGood || mode == kTwoPassBest) + passes_ = 2; + else + passes_ = 1; +} + +void EncoderTest::RunLoop(VideoSource *video) { + for (unsigned int pass = 0; pass < passes_; pass++) { + if (passes_ == 1) + cfg_.g_pass = VPX_RC_ONE_PASS; + else if (pass == 0) + cfg_.g_pass = VPX_RC_FIRST_PASS; + else + cfg_.g_pass = VPX_RC_LAST_PASS; + + BeginPassHook(pass); + Encoder encoder(cfg_, deadline_, &stats_); + + bool again; + + for (video->Begin(), again = true; again; video->Next()) { + again = video->img() != NULL; + + PreEncodeFrameHook(video); + encoder.EncodeFrame(video, flags_); + + CxDataIterator iter = encoder.GetCxData(); + + while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) { + again = true; + + if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) + continue; + + FramePktHook(pkt); + } + + if (!Continue()) + break; + } + + EndPassHook(); + + if (!Continue()) + break; + } +} + +} // namespace libvpx_test diff --git a/test/encode_test_driver.h b/test/encode_test_driver.h new file mode 100644 index 000000000..43faaaed7 --- /dev/null +++ b/test/encode_test_driver.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef TEST_ENCODE_TEST_DRIVER_H_ +#define TEST_ENCODE_TEST_DRIVER_H_ +#include +#include +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "vpx/vpx_encoder.h" +#include "vpx/vp8cx.h" + +namespace libvpx_test { + +class VideoSource; + +enum TestMode { + kRealTime, + kOnePassGood, + kOnePassBest, + kTwoPassGood, + kTwoPassBest +}; +#define ALL_TEST_MODES ::testing::Values(::libvpx_test::kRealTime, \ + ::libvpx_test::kOnePassGood, \ + ::libvpx_test::kOnePassBest, \ + ::libvpx_test::kTwoPassGood, \ + ::libvpx_test::kTwoPassBest) + + +// Provides an object to handle the libvpx get_cx_data() iteration pattern +class CxDataIterator { + public: + explicit CxDataIterator(vpx_codec_ctx_t *encoder) + : encoder_(encoder), iter_(NULL) {} + + const vpx_codec_cx_pkt_t *Next() { + return vpx_codec_get_cx_data(encoder_, &iter_); + } + + private: + vpx_codec_ctx_t *encoder_; + vpx_codec_iter_t iter_; +}; + + +// Implements an in-memory store for libvpx twopass statistics +class TwopassStatsStore { + public: + void Append(const vpx_codec_cx_pkt_t &pkt) { + buffer_.append(reinterpret_cast(pkt.data.twopass_stats.buf), + pkt.data.twopass_stats.sz); + } + + vpx_fixed_buf_t buf() { + const vpx_fixed_buf_t buf = { &buffer_[0], buffer_.size() }; + return buf; + } + + protected: + std::string buffer_; +}; + + +// Provides a simplified interface to manage one video encoding pass, given +// a configuration and video source. +// +// TODO(jkoleszar): The exact services it provides and the appropriate +// level of abstraction will be fleshed out as more tests are written. +class Encoder { + public: + Encoder(vpx_codec_enc_cfg_t cfg, unsigned long deadline, + TwopassStatsStore *stats) + : cfg_(cfg), deadline_(deadline), stats_(stats) { + memset(&encoder_, 0, sizeof(encoder_)); + } + + ~Encoder() { + vpx_codec_destroy(&encoder_); + } + + CxDataIterator GetCxData() { + return CxDataIterator(&encoder_); + } + + // This is a thin wrapper around vpx_codec_encode(), so refer to + // vpx_encoder.h for its semantics. + void EncodeFrame(VideoSource *video, unsigned long flags); + + // Convenience wrapper for EncodeFrame() + void EncodeFrame(VideoSource *video) { + EncodeFrame(video, 0); + } + + void set_deadline(unsigned long deadline) { + deadline_ = deadline; + } + + protected: + const char *EncoderError() { + const char *detail = vpx_codec_error_detail(&encoder_); + return detail ? detail : vpx_codec_error(&encoder_); + } + + // Encode an image + void EncodeFrameInternal(const VideoSource &video, unsigned long flags); + + // Flush the encoder on EOS + void Flush(); + + vpx_codec_ctx_t encoder_; + vpx_codec_enc_cfg_t cfg_; + unsigned long deadline_; + TwopassStatsStore *stats_; +}; + + +// Common test functionality for all Encoder tests. +// +// This class is a mixin which provides the main loop common to all +// encoder tests. It provides hooks which can be overridden by subclasses +// to implement each test's specific behavior, while centralizing the bulk +// of the boilerplate. Note that it doesn't inherit the gtest testing +// classes directly, so that tests can be parameterized differently. +class EncoderTest { + protected: + EncoderTest() : abort_(false), flags_(0) {} + + virtual ~EncoderTest() {} + + // Initialize the cfg_ member with the default configuration. + void InitializeConfig() { + const vpx_codec_err_t res = vpx_codec_enc_config_default( + &vpx_codec_vp8_cx_algo, &cfg_, 0); + ASSERT_EQ(VPX_CODEC_OK, res); + } + + // Map the TestMode enum to the deadline_ and passes_ variables. + void SetMode(TestMode mode); + + // Main loop. + virtual void RunLoop(VideoSource *video); + + // Hook to be called at the beginning of a pass. + virtual void BeginPassHook(unsigned int pass) {} + + // Hook to be called at the end of a pass. + virtual void EndPassHook() {} + + // Hook to be called before encoding a frame. + virtual void PreEncodeFrameHook(VideoSource *video) {} + + // Hook to be called on every compressed data packet. + virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {} + + // Hook to determine whether the encode loop should continue. + virtual bool Continue() const { return !abort_; } + + bool abort_; + vpx_codec_enc_cfg_t cfg_; + unsigned int passes_; + unsigned long deadline_; + TwopassStatsStore stats_; + unsigned long flags_; +}; + +} // namespace libvpx_test + +// Macros to be used with ::testing::Combine +#define PARAMS(...) ::testing::TestWithParam< std::tr1::tuple< __VA_ARGS__ > > +#define GET_PARAM(k) std::tr1::get< k >(GetParam()) + +#endif // TEST_ENCODE_TEST_DRIVER_H_ diff --git a/test/keyframe_test.cc b/test/keyframe_test.cc new file mode 100644 index 000000000..db18e5dab --- /dev/null +++ b/test/keyframe_test.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include +#include +#include "test/encode_test_driver.h" +#include "test/video_source.h" +#include "third_party/googletest/src/include/gtest/gtest.h" + +namespace { + +class KeyframeTest : public ::libvpx_test::EncoderTest, + public ::testing::TestWithParam { + protected: + virtual void SetUp() { + InitializeConfig(); + SetMode(GetParam()); + kf_count_ = 0; + kf_count_max_ = INT_MAX; + kf_do_force_kf_ = false; + } + + virtual bool Continue() { + return !HasFatalFailure() && !abort_; + } + + virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video) { + if (kf_do_force_kf_) + flags_ = (video->frame() % 3) ? 0 : VPX_EFLAG_FORCE_KF; + } + + virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) { + if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { + kf_pts_list_.push_back(pkt->data.frame.pts); + kf_count_++; + abort_ |= kf_count_ > kf_count_max_; + } + } + + bool kf_do_force_kf_; + int kf_count_; + int kf_count_max_; + std::vector< vpx_codec_pts_t > kf_pts_list_; +}; + +TEST_P(KeyframeTest, TestRandomVideoSource) { + // Validate that encoding the RandomVideoSource produces multiple keyframes. + // This validates the results of the TestDisableKeyframes test. + kf_count_max_ = 2; // early exit successful tests. + + ::libvpx_test::RandomVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + EXPECT_GT(kf_count_, 1); +} + +TEST_P(KeyframeTest, TestDisableKeyframes) { + cfg_.kf_mode = VPX_KF_DISABLED; + kf_count_max_ = 1; // early exit failed tests. + + ::libvpx_test::RandomVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + EXPECT_EQ(1, kf_count_); +} + +TEST_P(KeyframeTest, TestForceKeyframe) { + cfg_.kf_mode = VPX_KF_DISABLED; + kf_do_force_kf_ = true; + + ::libvpx_test::DummyVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + // verify that every third frame is a keyframe. + for (std::vector::iterator iter = kf_pts_list_.begin(); + iter != kf_pts_list_.end(); + ++iter) { + ASSERT_EQ(0, *iter % 3) << "Unexpected keyframe at frame " << *iter; + } +} + +TEST_P(KeyframeTest, TestKeyframeMaxDistance) { + cfg_.kf_max_dist = 25; + + ::libvpx_test::DummyVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + // verify that keyframe interval matches kf_max_dist + for (std::vector::iterator iter = kf_pts_list_.begin(); + iter != kf_pts_list_.end(); + iter++) { + ASSERT_EQ(0, *iter % 25) << "Unexpected keyframe at frame " << *iter; + } +} + +INSTANTIATE_TEST_CASE_P(AllModes, KeyframeTest, ALL_TEST_MODES); +} // namespace diff --git a/test/test.mk b/test/test.mk index 51382ee96..d04ad170f 100644 --- a/test/test.mk +++ b/test/test.mk @@ -1,4 +1,8 @@ LIBVPX_TEST_SRCS-yes += test.mk LIBVPX_TEST_SRCS-yes += boolcoder_test.cc +LIBVPX_TEST_SRCS-yes += encode_test_driver.cc +LIBVPX_TEST_SRCS-yes += encode_test_driver.h LIBVPX_TEST_SRCS-yes += idctllm_test.cc +LIBVPX_TEST_SRCS-yes += keyframe_test.cc LIBVPX_TEST_SRCS-yes += test_libvpx.cc +LIBVPX_TEST_SRCS-yes += video_source.h diff --git a/test/video_source.h b/test/video_source.h new file mode 100644 index 000000000..86c6caa83 --- /dev/null +++ b/test/video_source.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef TEST_VIDEO_SOURCE_H_ +#define TEST_VIDEO_SOURCE_H_ +#include "vpx/vpx_encoder.h" + +namespace libvpx_test { + +// Abstract base class for test video sources, which provide a stream of +// vpx_image_t images with associated timestamps and duration. +class VideoSource { + public: + virtual ~VideoSource() {} + + // Prepare the stream for reading, rewind/open as necessary. + virtual void Begin() = 0; + + // Advance the cursor to the next frame + virtual void Next() = 0; + + // Get the current video frame, or NULL on End-Of-Stream. + virtual vpx_image_t *img() const = 0; + + // Get the presentation timestamp of the current frame. + virtual vpx_codec_pts_t pts() const = 0; + + // Get the current frame's duration + virtual unsigned long duration() const = 0; + + // Get the timebase for the stream + virtual vpx_rational_t timebase() const = 0; + + // Get the current frame counter, starting at 0. + virtual unsigned int frame() const = 0; +}; + + +class DummyVideoSource : public VideoSource { + public: + DummyVideoSource() + : img_(NULL), limit_(100) { SetSize(80, 64); } + + virtual ~DummyVideoSource() { vpx_img_free(img_); } + + virtual void Begin() { + frame_ = 0; + FillFrame(); + } + + virtual void Next() { + ++frame_; + FillFrame(); + } + + virtual vpx_image_t *img() const { + return (frame_ < limit_) ? img_ : NULL; + } + + // Models a stream where Timebase = 1/FPS, so pts == frame. + virtual vpx_codec_pts_t pts() const { return frame_; } + + virtual unsigned long duration() const { return 1; } + + virtual vpx_rational_t timebase() const { + const vpx_rational_t t = {1, 30}; + return t; + } + + virtual unsigned int frame() const { return frame_; } + + void SetSize(unsigned int width, unsigned int height) { + vpx_img_free(img_); + raw_sz_ = ((width + 31)&~31) * height * 3 / 2; + img_ = vpx_img_alloc(NULL, VPX_IMG_FMT_VPXI420, width, height, 32); + } + + protected: + virtual void FillFrame() { memset(img_->img_data, 0, raw_sz_); } + + vpx_image_t *img_; + size_t raw_sz_; + unsigned int limit_; + unsigned int frame_; +}; + + +class RandomVideoSource : public DummyVideoSource { + protected: + // 15 frames of noise, followed by 15 static frames. Reset to 0 rather + // than holding previous frames to encourage keyframes to be thrown. + virtual void FillFrame() { + if (frame_ % 30 < 15) + for (size_t i = 0; i < raw_sz_; ++i) + img_->img_data[i] = rand(); + else + memset(img_->img_data, 0, raw_sz_); + } +}; + +} // namespace libvpx_test + +#endif // TEST_VIDEO_SOURCE_H_