├── .gitignore ├── Doxyfile ├── LICENSE.txt ├── Makefile ├── README.markdown ├── UnitTest++ ├── COPYING ├── Makefile ├── README └── src │ ├── AssertException.cpp │ ├── AssertException.h │ ├── CheckMacros.h │ ├── Checks.cpp │ ├── Checks.h │ ├── Config.h │ ├── CurrentTest.cpp │ ├── CurrentTest.h │ ├── DeferredTestReporter.cpp │ ├── DeferredTestReporter.h │ ├── DeferredTestResult.cpp │ ├── DeferredTestResult.h │ ├── ExecuteTest.h │ ├── MemoryOutStream.cpp │ ├── MemoryOutStream.h │ ├── Posix │ ├── SignalTranslator.cpp │ ├── SignalTranslator.h │ ├── TimeHelpers.cpp │ └── TimeHelpers.h │ ├── ReportAssert.cpp │ ├── ReportAssert.h │ ├── Test.cpp │ ├── Test.h │ ├── TestDetails.cpp │ ├── TestDetails.h │ ├── TestList.cpp │ ├── TestList.h │ ├── TestMacros.h │ ├── TestReporter.cpp │ ├── TestReporter.h │ ├── TestReporterStdout.cpp │ ├── TestReporterStdout.h │ ├── TestResults.cpp │ ├── TestResults.h │ ├── TestRunner.cpp │ ├── TestRunner.h │ ├── TestSuite.h │ ├── TimeConstraint.cpp │ ├── TimeConstraint.h │ ├── TimeHelpers.h │ ├── UnitTest++.h │ ├── Win32 │ ├── TimeHelpers.cpp │ └── TimeHelpers.h │ ├── XmlTestReporter.cpp │ ├── XmlTestReporter.h │ └── tests │ ├── Main.cpp │ ├── RecordingReporter.h │ ├── ScopedCurrentTest.h │ ├── TestAssertHandler.cpp │ ├── TestCheckMacros.cpp │ ├── TestChecks.cpp │ ├── TestCurrentTest.cpp │ ├── TestDeferredTestReporter.cpp │ ├── TestMemoryOutStream.cpp │ ├── TestTest.cpp │ ├── TestTestList.cpp │ ├── TestTestMacros.cpp │ ├── TestTestResults.cpp │ ├── TestTestRunner.cpp │ ├── TestTestSuite.cpp │ ├── TestTimeConstraint.cpp │ ├── TestTimeConstraintMacro.cpp │ ├── TestUnitTest++.cpp │ └── TestXmlTestReporter.cpp ├── inih ├── LICENSE.txt ├── README.txt ├── ini.c └── ini.h ├── screenshot.png ├── src ├── actions.h ├── clientmodel-events.cpp ├── clientmodel-events.h ├── common.h ├── configparse.cpp ├── configparse.h ├── logging │ ├── file.cpp │ ├── file.h │ ├── logging.cpp │ ├── logging.h │ ├── stream.cpp │ ├── stream.h │ ├── syslog.cpp │ └── syslog.h ├── model │ ├── changes.cpp │ ├── changes.h │ ├── client-model.cpp │ ├── client-model.h │ ├── desktop-type.h │ ├── focus-cycle.cpp │ ├── focus-cycle.h │ ├── screen.cpp │ ├── screen.h │ ├── unique-multimap.h │ ├── x-model.cpp │ └── x-model.h ├── smallwm.cpp ├── utils.cpp ├── utils.h ├── x-events.cpp ├── x-events.h ├── xdata.cpp └── xdata.h └── test ├── changes.cpp ├── client-model.cpp ├── configparse.cpp ├── focus-cycle.cpp ├── screen.cpp ├── test.cpp ├── unique-multimap.cpp ├── utils.cpp └── x-model.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | tags 4 | **/libUnitTest++.a 5 | *.d 6 | *.o 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Adam Marchetti 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # C related flags. This project is mostly C++ at this point, but it still uses 2 | # the C API provided by inih. 3 | CC=/usr/bin/gcc 4 | CFLAGS=-O3 5 | 6 | # C++ related flags. Note that you may need to modify the CXXFLAGS variable to 7 | # get SmallWM to build with Clang++. 8 | CXX=/usr/bin/g++ 9 | CXXFLAGS=-g -IUnitTest++/src -Itest -Iinih -Isrc -Wold-style-cast --std=c++11 10 | LINKERFLAGS=-lX11 -lXrandr 11 | 12 | # Binaries are classified into two groups - ${BINS} includes the main smallwm 13 | # binary only, while ${TESTS} includes all the binaries for the test suite. 14 | BINS=bin/smallwm 15 | TESTS=$(patsubst test/%.cpp,bin/test-%,$(wildcard test/*.cpp)) 16 | 17 | # We need to use := do to immediate evaluation. Since inih/ini.c is not with 18 | # the rest of the C sources files, we handle it as an explicit case at the end. 19 | # 20 | # Makefile variables with = are 'lazy', and thus self-reference doesn't work. 21 | # If we do the naive thing and just use += on the second set, then inih/ini.c 22 | # is included in ${OBJS} because the $(patsubst ...) expression cannot match 23 | # it. 24 | BASE_CFILES:=$(wildcard src/*.cpp) 25 | BASE_OBJS:=$(patsubst src/%.cpp,obj/%.o,${BASE_CFILES}) 26 | 27 | LOGGING_CFILES:=$(wildcard src/logging/*.cpp) 28 | LOGGING_OBJS:=$(patsubst src/logging/%.cpp,obj/logging/%.o,${LOGGING_CFILES}) 29 | 30 | MODEL_CFILES:=$(wildcard src/model/*.cpp) 31 | MODEL_OBJS:=$(patsubst src/model/%.cpp,obj/model/%.o,${MODEL_CFILES}) 32 | 33 | INI_CFILES:=inih/ini.c 34 | INI_OBJS:=obj/ini.o 35 | 36 | CFILES:=${BASE_CFILES} ${LOGGING_CFILES} ${MODEL_CFILES} ${INI_CFILES} 37 | OBJS:=${BASE_OBJS} ${LOGGING_OBJS} ${MODEL_OBJS} ${INI_OBJS} 38 | 39 | # ${HEADERS} exists mostly to make building Doxygen output more consistent 40 | # since a change in the headers may require the API documentation to be 41 | # re-created. 42 | HEADERS=$(wildcard src/*.h src/model/*.h) 43 | 44 | all: bin/smallwm 45 | 46 | # Used to probe for compiler errors, without linking everything 47 | check: obj ${OBJS} 48 | 49 | doc: ${HEADERS} ${SRCS} 50 | doxygen 51 | 52 | bin: 53 | [ -d bin ] || mkdir bin 54 | 55 | obj: 56 | [ -d obj ] || mkdir obj 57 | [ -d obj/model ] || mkdir obj/model 58 | [ -d obj/logging ] || mkdir obj/logging 59 | 60 | test: ${TESTS} 61 | for TEST in ${TESTS}; do echo "Running $$TEST::"; ./$$TEST; done 62 | 63 | tags: ${HEADRES} ${CFILES} 64 | ctags --c++-kinds=+p --fields=+iaS --extra=+q --language-force=c++ -R src 65 | 66 | clean: 67 | rm -rf bin obj doc tags 68 | 69 | bin/smallwm: bin obj ${OBJS} 70 | ${CXX} ${CXXFLAGS} ${OBJS} ${LINKERFLAGS} -o bin/smallwm 71 | 72 | obj/ini.o: obj inih/ini.c 73 | ${CC} ${CFLAGS} -c inih/ini.c -o obj/ini.o 74 | 75 | obj/%.o: obj 76 | 77 | obj/%.o: src/%.cpp 78 | ${CXX} ${CXXFLAGS} -c $< -o $@ 79 | 80 | obj/logging/%.o: src/logging/%.cpp 81 | ${CXX} ${CXXFLAGS} -c $< -o $@ 82 | 83 | obj/model/%.o: src/model/%.cpp 84 | ${CXX} ${CXXFLAGS} -c $< -o $@ 85 | 86 | # Getting unit tests to build is a bit awkward. Since I want to avoid 87 | # distributing a static library along with SmallWM, it is necessary to build 88 | # UnitTest++ on-demand from source. Hence, recursive make... 89 | bin/libUnitTest++.a: bin 90 | export CXX 91 | $(MAKE) -C UnitTest++ libUnitTest++.a 92 | cp UnitTest++/libUnitTest++.a bin 93 | 94 | # This simply ensure that the test itself builds properly. It doesn't do 95 | # anything, since if it builds, the test is a success. 96 | bin/test-test: bin bin/libUnitTest++.a test/test.cpp 97 | ${CXX} ${CXXFLAGS} test/test.cpp bin/libUnitTest++.a -o bin/test-test 98 | 99 | bin/test-changes: bin/libUnitTest++.a obj/test-changes.o obj/model/changes.o 100 | ${CXX} ${CXXFLAGS} obj/test-changes.o bin/libUnitTest++.a obj/model/changes.o -o bin/test-changes 101 | 102 | obj/test-changes.o: obj test/changes.cpp 103 | ${CXX} ${CXXFLAGS} -c test/changes.cpp -o obj/test-changes.o 104 | 105 | bin/test-configparse: bin/libUnitTest++.a obj/test-configparse.o obj/ini.o obj/configparse.o obj/utils.o 106 | ${CXX} ${CXXFLAGS} obj/test-configparse.o bin/libUnitTest++.a obj/configparse.o obj/ini.o obj/utils.o ${LINKERFLAGS} -o bin/test-configparse 107 | 108 | obj/test-configparse.o: obj test/configparse.cpp 109 | ${CXX} ${CXXFLAGS} -c test/configparse.cpp -o obj/test-configparse.o 110 | 111 | bin/test-client-model: bin/libUnitTest++.a obj/test-client-model.o obj/model/client-model.o obj/model/changes.o obj/model/screen.o obj/model/focus-cycle.o 112 | ${CXX} ${CXXFLAGS} obj/test-client-model.o bin/libUnitTest++.a obj/model/client-model.o obj/model/changes.o obj/model/screen.o obj/model/focus-cycle.o ${LINKER_FLAGS} -o bin/test-client-model 113 | 114 | obj/test-client-model.o: obj test/client-model.cpp src/model/changes.h src/model/client-model.h src/model/desktop-type.h src/model/screen.h src/model/unique-multimap.h 115 | ${CXX} ${CXXFLAGS} -c test/client-model.cpp -o obj/test-client-model.o 116 | 117 | bin/test-focus-cycle: bin/libUnitTest++.a obj/test-focus-cycle.o obj/model/focus-cycle.o obj/logging/logging.o obj/logging/stream.o 118 | ${CXX} ${CXXFLAGS} obj/test-focus-cycle.o obj/model/focus-cycle.o obj/logging/logging.o obj/logging/stream.o bin/libUnitTest++.a ${LINKER_FLAGS} -o bin/test-focus-cycle 119 | 120 | obj/test-focus-cycle.o: obj 121 | ${CXX} ${CXXFLAGS} -c test/focus-cycle.cpp -o obj/test-focus-cycle.o 122 | 123 | bin/test-x-model: bin/libUnitTest++.a obj/test-x-model.o obj/model/x-model.o 124 | ${CXX} ${CXXFLAGS} obj/test-x-model.o bin/libUnitTest++.a obj/model/x-model.o ${LINKER_FLAGS} -o bin/test-x-model 125 | 126 | obj/test-x-model.o: obj test/x-model.cpp src/model/x-model.h 127 | ${CXX} ${CXXFLAGS} -c test/x-model.cpp -o obj/test-x-model.o 128 | 129 | bin/test-screen: bin/libUnitTest++.a obj/test-screen.o obj/model/screen.o 130 | ${CXX} ${CXXFLAGS} obj/test-screen.o bin/libUnitTest++.a obj/model/screen.o ${LINKER_FLAGS} -o bin/test-screen 131 | 132 | obj/test-screen.o: obj test/screen.cpp src/model/screen.cpp 133 | ${CXX} ${CXXFLAGS} -c test/screen.cpp -o obj/test-screen.o 134 | 135 | bin/test-utils: bin/libUnitTest++.a obj/test-utils.o obj/utils.o 136 | ${CXX} ${CXXFLAGS} obj/test-utils.o bin/libUnitTest++.a obj/utils.o -o bin/test-utils 137 | 138 | obj/test-utils.o: obj test/utils.cpp 139 | ${CXX} ${CXXFLAGS} -c test/utils.cpp -o obj/test-utils.o 140 | 141 | bin/test-unique-multimap: bin/libUnitTest++.a obj/test-unique-multimap.o 142 | ${CXX} ${CXXFLAGS} obj/test-unique-multimap.o bin/libUnitTest++.a -o bin/test-unique-multimap 143 | 144 | obj/test-unique-multimap.o: obj test/unique-multimap.cpp 145 | ${CXX} ${CXXFLAGS} -c test/unique-multimap.cpp -o obj/test-unique-multimap.o 146 | -------------------------------------------------------------------------------- /UnitTest++/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006 Noel Llopis and Charles Nicholson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /UnitTest++/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | CXXFLAGS ?= -g -Wall -W -ansi # -pedantic 3 | LDFLAGS ?= 4 | SED = sed 5 | MV = mv 6 | RM = rm 7 | 8 | .SUFFIXES: .o .cpp 9 | 10 | lib = libUnitTest++.a 11 | test = TestUnitTest++ 12 | 13 | src = src/AssertException.cpp \ 14 | src/Test.cpp \ 15 | src/Checks.cpp \ 16 | src/TestRunner.cpp \ 17 | src/TestResults.cpp \ 18 | src/TestReporter.cpp \ 19 | src/TestReporterStdout.cpp \ 20 | src/ReportAssert.cpp \ 21 | src/TestList.cpp \ 22 | src/TimeConstraint.cpp \ 23 | src/TestDetails.cpp \ 24 | src/MemoryOutStream.cpp \ 25 | src/DeferredTestReporter.cpp \ 26 | src/DeferredTestResult.cpp \ 27 | src/XmlTestReporter.cpp \ 28 | src/CurrentTest.cpp 29 | 30 | ifeq ($(MSYSTEM), MINGW32) 31 | src += src/Win32/TimeHelpers.cpp 32 | else 33 | src += src/Posix/SignalTranslator.cpp \ 34 | src/Posix/TimeHelpers.cpp 35 | endif 36 | 37 | test_src = src/tests/Main.cpp \ 38 | src/tests/TestAssertHandler.cpp \ 39 | src/tests/TestChecks.cpp \ 40 | src/tests/TestUnitTest++.cpp \ 41 | src/tests/TestTest.cpp \ 42 | src/tests/TestTestResults.cpp \ 43 | src/tests/TestTestRunner.cpp \ 44 | src/tests/TestCheckMacros.cpp \ 45 | src/tests/TestTestList.cpp \ 46 | src/tests/TestTestMacros.cpp \ 47 | src/tests/TestTimeConstraint.cpp \ 48 | src/tests/TestTimeConstraintMacro.cpp \ 49 | src/tests/TestMemoryOutStream.cpp \ 50 | src/tests/TestDeferredTestReporter.cpp \ 51 | src/tests/TestXmlTestReporter.cpp \ 52 | src/tests/TestCurrentTest.cpp 53 | 54 | objects = $(patsubst %.cpp, %.o, $(src)) 55 | test_objects = $(patsubst %.cpp, %.o, $(test_src)) 56 | dependencies = $(subst .o,.d,$(objects)) 57 | test_dependencies = $(subst .o,.d,$(test_objects)) 58 | 59 | define make-depend 60 | $(CXX) $(CXXFLAGS) -M $1 | \ 61 | $(SED) -e 's,\($(notdir $2)\) *:,$(dir $2)\1: ,' > $3.tmp 62 | $(SED) -e 's/#.*//' \ 63 | -e 's/^[^:]*: *//' \ 64 | -e 's/ *\\$$//' \ 65 | -e '/^$$/ d' \ 66 | -e 's/$$/ :/' $3.tmp >> $3.tmp 67 | $(MV) $3.tmp $3 68 | endef 69 | 70 | 71 | all: $(test) 72 | 73 | 74 | $(lib): $(objects) 75 | @echo Creating $(lib) library... 76 | @ar cr $(lib) $(objects) 77 | 78 | $(test): $(lib) $(test_objects) 79 | @echo Linking $(test)... 80 | @$(CXX) $(LDFLAGS) -o $(test) $(test_objects) $(lib) 81 | @echo Running unit tests... 82 | @./$(test) 83 | 84 | clean: 85 | -@$(RM) $(objects) $(test_objects) $(dependencies) $(test_dependencies) $(test) $(lib) 2> /dev/null 86 | 87 | %.o : %.cpp 88 | @echo $< 89 | @$(call make-depend,$<,$@,$(subst .o,.d,$@)) 90 | @$(CXX) $(CXXFLAGS) -c $< -o $(patsubst %.cpp, %.o, $<) 91 | 92 | 93 | ifneq "$(MAKECMDGOALS)" "clean" 94 | -include $(dependencies) 95 | -include $(test_dependencies) 96 | endif 97 | -------------------------------------------------------------------------------- /UnitTest++/README: -------------------------------------------------------------------------------- 1 | UnitTest++ README 2 | Version: v1.4 3 | Last update: 2008-10-30 4 | 5 | UnitTest++ is free software. You may copy, distribute, and modify it under 6 | the terms of the License contained in the file COPYING distributed 7 | with this package. This license is the same as the MIT/X Consortium 8 | license. 9 | 10 | See src/tests/TestUnitTest++.cpp for usage. 11 | 12 | Authors: 13 | Noel Llopis (llopis@convexhull.com) 14 | Charles Nicholson (charles.nicholson@gmail.com) 15 | 16 | Contributors: 17 | Jim Tilander 18 | Kim Grasman 19 | Jonathan Jansson 20 | Dirck Blaskey 21 | Rory Driscoll 22 | Dan Lind 23 | Matt Kimmel -- Submitted with permission from Blue Fang Games 24 | Anthony Moralez 25 | Jeff Dixon 26 | Randy Coulman 27 | Lieven van der Heide 28 | 29 | Release notes: 30 | -------------- 31 | Version 1.4 (2008-10-30) 32 | - CHECK macros work at arbitrary stack depth from inside TESTs. 33 | - Remove obsolete TEST_UTILITY macros 34 | - Predicated test execution (via TestRunner::RunTestsIf) 35 | - Better exception handling for fixture ctors/dtors. 36 | - VC6/7/8/9 support 37 | 38 | Version 1.3 (2007-4-22) 39 | - Removed dynamic memory allocations (other than streams) 40 | - MinGW support 41 | - Consistent (native) line endings 42 | - Minor bug fixing 43 | 44 | Version 1.2 (2006-10-29) 45 | - First pass at documentation. 46 | - More detailed error crash catching in fixtures. 47 | - Standard streams used for printing objects under check. This should allow the 48 | use of standard class types such as std::string or other custom classes with 49 | stream operators to ostream. 50 | - Standard streams can be optionally compiled off by defining UNITTEST_USE_CUSTOM_STREAMS 51 | in Config.h 52 | - Added named test suites 53 | - Added CHECK_ARRAY2D_CLOSE 54 | - Posix library name is libUnitTest++.a now 55 | - Floating point numbers are postfixed with f in the failure reports 56 | 57 | Version 1.1 (2006-04-18) 58 | - CHECK macros do not have side effects even if one of the parameters changes state 59 | - Removed CHECK_ARRAY_EQUAL (too similar to CHECK_ARRAY_CLOSE) 60 | - Added local and global time constraints 61 | - Removed dependencies on strstream 62 | - Improved Posix signal to exception translator 63 | - Failing tests are added to Visual Studio's error list 64 | - Fixed Visual Studio projects to work with spaces in directories 65 | 66 | Version 1.0 (2006-03-15) 67 | - Initial release 68 | 69 | -------------------------------------------------------------------------------- /UnitTest++/src/AssertException.cpp: -------------------------------------------------------------------------------- 1 | #include "AssertException.h" 2 | #include 3 | 4 | namespace UnitTest { 5 | 6 | AssertException::AssertException(char const* description, char const* filename, int lineNumber) 7 | : m_lineNumber(lineNumber) 8 | { 9 | using namespace std; 10 | 11 | strcpy(m_description, description); 12 | strcpy(m_filename, filename); 13 | } 14 | 15 | AssertException::~AssertException() throw() 16 | { 17 | } 18 | 19 | char const* AssertException::what() const throw() 20 | { 21 | return m_description; 22 | } 23 | 24 | char const* AssertException::Filename() const 25 | { 26 | return m_filename; 27 | } 28 | 29 | int AssertException::LineNumber() const 30 | { 31 | return m_lineNumber; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /UnitTest++/src/AssertException.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_ASSERTEXCEPTION_H 2 | #define UNITTEST_ASSERTEXCEPTION_H 3 | 4 | #include 5 | 6 | 7 | namespace UnitTest { 8 | 9 | class AssertException : public std::exception 10 | { 11 | public: 12 | AssertException(char const* description, char const* filename, int lineNumber); 13 | virtual ~AssertException() throw(); 14 | 15 | virtual char const* what() const throw(); 16 | 17 | char const* Filename() const; 18 | int LineNumber() const; 19 | 20 | private: 21 | char m_description[512]; 22 | char m_filename[256]; 23 | int m_lineNumber; 24 | }; 25 | 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /UnitTest++/src/CheckMacros.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_CHECKMACROS_H 2 | #define UNITTEST_CHECKMACROS_H 3 | 4 | #include "Checks.h" 5 | #include "AssertException.h" 6 | #include "MemoryOutStream.h" 7 | #include "TestDetails.h" 8 | #include "CurrentTest.h" 9 | 10 | #ifdef CHECK 11 | #error UnitTest++ redefines CHECK 12 | #endif 13 | 14 | #ifdef CHECK_EQUAL 15 | #error UnitTest++ redefines CHECK_EQUAL 16 | #endif 17 | 18 | #ifdef CHECK_CLOSE 19 | #error UnitTest++ redefines CHECK_CLOSE 20 | #endif 21 | 22 | #ifdef CHECK_ARRAY_EQUAL 23 | #error UnitTest++ redefines CHECK_ARRAY_EQUAL 24 | #endif 25 | 26 | #ifdef CHECK_ARRAY_CLOSE 27 | #error UnitTest++ redefines CHECK_ARRAY_CLOSE 28 | #endif 29 | 30 | #ifdef CHECK_ARRAY2D_CLOSE 31 | #error UnitTest++ redefines CHECK_ARRAY2D_CLOSE 32 | #endif 33 | 34 | #define CHECK(value) \ 35 | do \ 36 | { \ 37 | try { \ 38 | if (!UnitTest::Check(value)) \ 39 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), #value); \ 40 | } \ 41 | catch (...) { \ 42 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \ 43 | "Unhandled exception in CHECK(" #value ")"); \ 44 | } \ 45 | } while (0) 46 | 47 | #define CHECK_EQUAL(expected, actual) \ 48 | do \ 49 | { \ 50 | try { \ 51 | UnitTest::CheckEqual(*UnitTest::CurrentTest::Results(), expected, actual, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \ 52 | } \ 53 | catch (...) { \ 54 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \ 55 | "Unhandled exception in CHECK_EQUAL(" #expected ", " #actual ")"); \ 56 | } \ 57 | } while (0) 58 | 59 | #define CHECK_CLOSE(expected, actual, tolerance) \ 60 | do \ 61 | { \ 62 | try { \ 63 | UnitTest::CheckClose(*UnitTest::CurrentTest::Results(), expected, actual, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \ 64 | } \ 65 | catch (...) { \ 66 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \ 67 | "Unhandled exception in CHECK_CLOSE(" #expected ", " #actual ")"); \ 68 | } \ 69 | } while (0) 70 | 71 | #define CHECK_ARRAY_EQUAL(expected, actual, count) \ 72 | do \ 73 | { \ 74 | try { \ 75 | UnitTest::CheckArrayEqual(*UnitTest::CurrentTest::Results(), expected, actual, count, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \ 76 | } \ 77 | catch (...) { \ 78 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \ 79 | "Unhandled exception in CHECK_ARRAY_EQUAL(" #expected ", " #actual ")"); \ 80 | } \ 81 | } while (0) 82 | 83 | #define CHECK_ARRAY_CLOSE(expected, actual, count, tolerance) \ 84 | do \ 85 | { \ 86 | try { \ 87 | UnitTest::CheckArrayClose(*UnitTest::CurrentTest::Results(), expected, actual, count, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \ 88 | } \ 89 | catch (...) { \ 90 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \ 91 | "Unhandled exception in CHECK_ARRAY_CLOSE(" #expected ", " #actual ")"); \ 92 | } \ 93 | } while (0) 94 | 95 | #define CHECK_ARRAY2D_CLOSE(expected, actual, rows, columns, tolerance) \ 96 | do \ 97 | { \ 98 | try { \ 99 | UnitTest::CheckArray2DClose(*UnitTest::CurrentTest::Results(), expected, actual, rows, columns, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \ 100 | } \ 101 | catch (...) { \ 102 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \ 103 | "Unhandled exception in CHECK_ARRAY_CLOSE(" #expected ", " #actual ")"); \ 104 | } \ 105 | } while (0) 106 | 107 | 108 | #define CHECK_THROW(expression, ExpectedExceptionType) \ 109 | do \ 110 | { \ 111 | bool caught_ = false; \ 112 | try { expression; } \ 113 | catch (ExpectedExceptionType const&) { caught_ = true; } \ 114 | catch (...) {} \ 115 | if (!caught_) \ 116 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), "Expected exception: \"" #ExpectedExceptionType "\" not thrown"); \ 117 | } while(0) 118 | 119 | #define CHECK_ASSERT(expression) \ 120 | CHECK_THROW(expression, UnitTest::AssertException); 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /UnitTest++/src/Checks.cpp: -------------------------------------------------------------------------------- 1 | #include "Checks.h" 2 | #include 3 | 4 | namespace UnitTest { 5 | 6 | namespace { 7 | 8 | void CheckStringsEqual(TestResults& results, char const* expected, char const* actual, 9 | TestDetails const& details) 10 | { 11 | using namespace std; 12 | 13 | if (strcmp(expected, actual)) 14 | { 15 | UnitTest::MemoryOutStream stream; 16 | stream << "Expected " << expected << " but was " << actual; 17 | 18 | results.OnTestFailure(details, stream.GetText()); 19 | } 20 | } 21 | 22 | } 23 | 24 | 25 | void CheckEqual(TestResults& results, char const* expected, char const* actual, 26 | TestDetails const& details) 27 | { 28 | CheckStringsEqual(results, expected, actual, details); 29 | } 30 | 31 | void CheckEqual(TestResults& results, char* expected, char* actual, 32 | TestDetails const& details) 33 | { 34 | CheckStringsEqual(results, expected, actual, details); 35 | } 36 | 37 | void CheckEqual(TestResults& results, char* expected, char const* actual, 38 | TestDetails const& details) 39 | { 40 | CheckStringsEqual(results, expected, actual, details); 41 | } 42 | 43 | void CheckEqual(TestResults& results, char const* expected, char* actual, 44 | TestDetails const& details) 45 | { 46 | CheckStringsEqual(results, expected, actual, details); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /UnitTest++/src/Checks.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_CHECKS_H 2 | #define UNITTEST_CHECKS_H 3 | 4 | #include "Config.h" 5 | #include "TestResults.h" 6 | #include "MemoryOutStream.h" 7 | 8 | namespace UnitTest { 9 | 10 | 11 | template< typename Value > 12 | bool Check(Value const value) 13 | { 14 | return !!value; // doing double negative to avoid silly VS warnings 15 | } 16 | 17 | 18 | template< typename Expected, typename Actual > 19 | void CheckEqual(TestResults& results, Expected const& expected, Actual const& actual, TestDetails const& details) 20 | { 21 | if (!(expected == actual)) 22 | { 23 | UnitTest::MemoryOutStream stream; 24 | stream << "Expected " << expected << " but was " << actual; 25 | 26 | results.OnTestFailure(details, stream.GetText()); 27 | } 28 | } 29 | 30 | void CheckEqual(TestResults& results, char const* expected, char const* actual, TestDetails const& details); 31 | 32 | void CheckEqual(TestResults& results, char* expected, char* actual, TestDetails const& details); 33 | 34 | void CheckEqual(TestResults& results, char* expected, char const* actual, TestDetails const& details); 35 | 36 | void CheckEqual(TestResults& results, char const* expected, char* actual, TestDetails const& details); 37 | 38 | template< typename Expected, typename Actual, typename Tolerance > 39 | bool AreClose(Expected const& expected, Actual const& actual, Tolerance const& tolerance) 40 | { 41 | return (actual >= (expected - tolerance)) && (actual <= (expected + tolerance)); 42 | } 43 | 44 | template< typename Expected, typename Actual, typename Tolerance > 45 | void CheckClose(TestResults& results, Expected const& expected, Actual const& actual, Tolerance const& tolerance, 46 | TestDetails const& details) 47 | { 48 | if (!AreClose(expected, actual, tolerance)) 49 | { 50 | UnitTest::MemoryOutStream stream; 51 | stream << "Expected " << expected << " +/- " << tolerance << " but was " << actual; 52 | 53 | results.OnTestFailure(details, stream.GetText()); 54 | } 55 | } 56 | 57 | 58 | template< typename Expected, typename Actual > 59 | void CheckArrayEqual(TestResults& results, Expected const& expected, Actual const& actual, 60 | int const count, TestDetails const& details) 61 | { 62 | bool equal = true; 63 | for (int i = 0; i < count; ++i) 64 | equal &= (expected[i] == actual[i]); 65 | 66 | if (!equal) 67 | { 68 | UnitTest::MemoryOutStream stream; 69 | 70 | stream << "Expected [ "; 71 | 72 | for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex) 73 | stream << expected[expectedIndex] << " "; 74 | 75 | stream << "] but was [ "; 76 | 77 | for (int actualIndex = 0; actualIndex < count; ++actualIndex) 78 | stream << actual[actualIndex] << " "; 79 | 80 | stream << "]"; 81 | 82 | results.OnTestFailure(details, stream.GetText()); 83 | } 84 | } 85 | 86 | template< typename Expected, typename Actual, typename Tolerance > 87 | bool ArrayAreClose(Expected const& expected, Actual const& actual, int const count, Tolerance const& tolerance) 88 | { 89 | bool equal = true; 90 | for (int i = 0; i < count; ++i) 91 | equal &= AreClose(expected[i], actual[i], tolerance); 92 | return equal; 93 | } 94 | 95 | template< typename Expected, typename Actual, typename Tolerance > 96 | void CheckArrayClose(TestResults& results, Expected const& expected, Actual const& actual, 97 | int const count, Tolerance const& tolerance, TestDetails const& details) 98 | { 99 | bool equal = ArrayAreClose(expected, actual, count, tolerance); 100 | 101 | if (!equal) 102 | { 103 | UnitTest::MemoryOutStream stream; 104 | 105 | stream << "Expected [ "; 106 | for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex) 107 | stream << expected[expectedIndex] << " "; 108 | stream << "] +/- " << tolerance << " but was [ "; 109 | 110 | for (int actualIndex = 0; actualIndex < count; ++actualIndex) 111 | stream << actual[actualIndex] << " "; 112 | stream << "]"; 113 | 114 | results.OnTestFailure(details, stream.GetText()); 115 | } 116 | } 117 | 118 | template< typename Expected, typename Actual, typename Tolerance > 119 | void CheckArray2DClose(TestResults& results, Expected const& expected, Actual const& actual, 120 | int const rows, int const columns, Tolerance const& tolerance, TestDetails const& details) 121 | { 122 | bool equal = true; 123 | for (int i = 0; i < rows; ++i) 124 | equal &= ArrayAreClose(expected[i], actual[i], columns, tolerance); 125 | 126 | if (!equal) 127 | { 128 | UnitTest::MemoryOutStream stream; 129 | 130 | stream << "Expected [ "; 131 | 132 | for (int expectedRow = 0; expectedRow < rows; ++expectedRow) 133 | { 134 | stream << "[ "; 135 | for (int expectedColumn = 0; expectedColumn < columns; ++expectedColumn) 136 | stream << expected[expectedRow][expectedColumn] << " "; 137 | stream << "] "; 138 | } 139 | 140 | stream << "] +/- " << tolerance << " but was [ "; 141 | 142 | for (int actualRow = 0; actualRow < rows; ++actualRow) 143 | { 144 | stream << "[ "; 145 | for (int actualColumn = 0; actualColumn < columns; ++actualColumn) 146 | stream << actual[actualRow][actualColumn] << " "; 147 | stream << "] "; 148 | } 149 | 150 | stream << "]"; 151 | 152 | results.OnTestFailure(details, stream.GetText()); 153 | } 154 | } 155 | 156 | } 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /UnitTest++/src/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_CONFIG_H 2 | #define UNITTEST_CONFIG_H 3 | 4 | // Standard defines documented here: http://predef.sourceforge.net 5 | 6 | #if defined(_MSC_VER) 7 | #pragma warning(disable:4127) // conditional expression is constant 8 | #pragma warning(disable:4702) // unreachable code 9 | #pragma warning(disable:4722) // destructor never returns, potential memory leak 10 | 11 | #if (_MSC_VER == 1200) // VC6 12 | #pragma warning(disable:4786) 13 | #pragma warning(disable:4290) 14 | #endif 15 | #endif 16 | 17 | #if defined(unix) || defined(__unix__) || defined(__unix) || defined(linux) || \ 18 | defined(__APPLE__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) 19 | #define UNITTEST_POSIX 20 | #endif 21 | 22 | #if defined(__MINGW32__) 23 | #define UNITTEST_MINGW 24 | #endif 25 | 26 | // by default, MemoryOutStream is implemented in terms of std::ostringstream, which can be expensive. 27 | // uncomment this line to use the custom MemoryOutStream (no deps on std::ostringstream). 28 | 29 | //#define UNITTEST_USE_CUSTOM_STREAMS 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /UnitTest++/src/CurrentTest.cpp: -------------------------------------------------------------------------------- 1 | #include "CurrentTest.h" 2 | #include 3 | 4 | namespace UnitTest { 5 | 6 | TestResults*& CurrentTest::Results() 7 | { 8 | static TestResults* testResults = NULL; 9 | return testResults; 10 | } 11 | 12 | const TestDetails*& CurrentTest::Details() 13 | { 14 | static const TestDetails* testDetails = NULL; 15 | return testDetails; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /UnitTest++/src/CurrentTest.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_CURRENTTESTRESULTS_H 2 | #define UNITTEST_CURRENTTESTRESULTS_H 3 | 4 | namespace UnitTest { 5 | 6 | class TestResults; 7 | class TestDetails; 8 | 9 | namespace CurrentTest 10 | { 11 | TestResults*& Results(); 12 | const TestDetails*& Details(); 13 | } 14 | 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /UnitTest++/src/DeferredTestReporter.cpp: -------------------------------------------------------------------------------- 1 | #include "DeferredTestReporter.h" 2 | #include "TestDetails.h" 3 | #include "Config.h" 4 | 5 | using namespace UnitTest; 6 | 7 | void DeferredTestReporter::ReportTestStart(TestDetails const& details) 8 | { 9 | m_results.push_back(DeferredTestResult(details.suiteName, details.testName)); 10 | } 11 | 12 | void DeferredTestReporter::ReportFailure(TestDetails const& details, char const* failure) 13 | { 14 | DeferredTestResult& r = m_results.back(); 15 | r.failed = true; 16 | r.failures.push_back(DeferredTestResult::Failure(details.lineNumber, failure)); 17 | r.failureFile = details.filename; 18 | } 19 | 20 | void DeferredTestReporter::ReportTestFinish(TestDetails const&, float secondsElapsed) 21 | { 22 | DeferredTestResult& r = m_results.back(); 23 | r.timeElapsed = secondsElapsed; 24 | } 25 | 26 | DeferredTestReporter::DeferredTestResultList& DeferredTestReporter::GetResults() 27 | { 28 | return m_results; 29 | } 30 | -------------------------------------------------------------------------------- /UnitTest++/src/DeferredTestReporter.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_DEFERREDTESTREPORTER_H 2 | #define UNITTEST_DEFERREDTESTREPORTER_H 3 | 4 | #include "TestReporter.h" 5 | #include "DeferredTestResult.h" 6 | 7 | #include 8 | 9 | namespace UnitTest 10 | { 11 | 12 | class DeferredTestReporter : public TestReporter 13 | { 14 | public: 15 | virtual void ReportTestStart(TestDetails const& details); 16 | virtual void ReportFailure(TestDetails const& details, char const* failure); 17 | virtual void ReportTestFinish(TestDetails const& details, float secondsElapsed); 18 | 19 | typedef std::vector< DeferredTestResult > DeferredTestResultList; 20 | DeferredTestResultList& GetResults(); 21 | 22 | private: 23 | DeferredTestResultList m_results; 24 | }; 25 | 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /UnitTest++/src/DeferredTestResult.cpp: -------------------------------------------------------------------------------- 1 | #include "DeferredTestResult.h" 2 | #include "Config.h" 3 | 4 | namespace UnitTest 5 | { 6 | 7 | DeferredTestResult::DeferredTestResult() 8 | : suiteName("") 9 | , testName("") 10 | , failureFile("") 11 | , timeElapsed(0.0f) 12 | , failed(false) 13 | { 14 | } 15 | 16 | DeferredTestResult::DeferredTestResult(char const* suite, char const* test) 17 | : suiteName(suite) 18 | , testName(test) 19 | , failureFile("") 20 | , timeElapsed(0.0f) 21 | , failed(false) 22 | { 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /UnitTest++/src/DeferredTestResult.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_DEFERREDTESTRESULT_H 2 | #define UNITTEST_DEFERREDTESTRESULT_H 3 | 4 | #include 5 | #include 6 | 7 | namespace UnitTest 8 | { 9 | 10 | struct DeferredTestResult 11 | { 12 | DeferredTestResult(); 13 | DeferredTestResult(char const* suite, char const* test); 14 | 15 | std::string suiteName; 16 | std::string testName; 17 | std::string failureFile; 18 | 19 | typedef std::pair< int, std::string > Failure; 20 | typedef std::vector< Failure > FailureVec; 21 | FailureVec failures; 22 | 23 | float timeElapsed; 24 | bool failed; 25 | }; 26 | 27 | } 28 | 29 | #endif //UNITTEST_DEFERREDTESTRESULT_H 30 | -------------------------------------------------------------------------------- /UnitTest++/src/ExecuteTest.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_EXECUTE_TEST_H 2 | #define UNITTEST_EXECUTE_TEST_H 3 | 4 | #include "TestDetails.h" 5 | #include "MemoryOutStream.h" 6 | #include "AssertException.h" 7 | #include "CurrentTest.h" 8 | 9 | #ifdef UNITTEST_POSIX 10 | #include "Posix/SignalTranslator.h" 11 | #endif 12 | 13 | namespace UnitTest { 14 | 15 | template< typename T > 16 | void ExecuteTest(T& testObject, TestDetails const& details) 17 | { 18 | CurrentTest::Details() = &details; 19 | 20 | try 21 | { 22 | #ifdef UNITTEST_POSIX 23 | UNITTEST_THROW_SIGNALS 24 | #endif 25 | testObject.RunImpl(); 26 | } 27 | catch (AssertException const& e) 28 | { 29 | CurrentTest::Results()->OnTestFailure( 30 | TestDetails(details.testName, details.suiteName, e.Filename(), e.LineNumber()), e.what()); 31 | } 32 | catch (std::exception const& e) 33 | { 34 | MemoryOutStream stream; 35 | stream << "Unhandled exception: " << e.what(); 36 | CurrentTest::Results()->OnTestFailure(details, stream.GetText()); 37 | } 38 | catch (...) 39 | { 40 | CurrentTest::Results()->OnTestFailure(details, "Unhandled exception: Crash!"); 41 | } 42 | } 43 | 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /UnitTest++/src/MemoryOutStream.cpp: -------------------------------------------------------------------------------- 1 | #include "MemoryOutStream.h" 2 | 3 | #ifndef UNITTEST_USE_CUSTOM_STREAMS 4 | 5 | 6 | namespace UnitTest { 7 | 8 | char const* MemoryOutStream::GetText() const 9 | { 10 | m_text = this->str(); 11 | return m_text.c_str(); 12 | } 13 | 14 | 15 | } 16 | 17 | 18 | #else 19 | 20 | 21 | #include 22 | #include 23 | 24 | namespace UnitTest { 25 | 26 | namespace { 27 | 28 | template 29 | void FormatToStream(MemoryOutStream& stream, char const* format, ValueType const& value) 30 | { 31 | using namespace std; 32 | 33 | char txt[32]; 34 | sprintf(txt, format, value); 35 | stream << txt; 36 | } 37 | 38 | int RoundUpToMultipleOfPow2Number (int n, int pow2Number) 39 | { 40 | return (n + (pow2Number - 1)) & ~(pow2Number - 1); 41 | } 42 | 43 | } 44 | 45 | 46 | MemoryOutStream::MemoryOutStream(int const size) 47 | : m_capacity (0) 48 | , m_buffer (0) 49 | 50 | { 51 | GrowBuffer(size); 52 | } 53 | 54 | MemoryOutStream::~MemoryOutStream() 55 | { 56 | delete [] m_buffer; 57 | } 58 | 59 | char const* MemoryOutStream::GetText() const 60 | { 61 | return m_buffer; 62 | } 63 | 64 | MemoryOutStream& MemoryOutStream::operator << (char const* txt) 65 | { 66 | using namespace std; 67 | 68 | int const bytesLeft = m_capacity - (int)strlen(m_buffer); 69 | int const bytesRequired = (int)strlen(txt) + 1; 70 | 71 | if (bytesRequired > bytesLeft) 72 | { 73 | int const requiredCapacity = bytesRequired + m_capacity - bytesLeft; 74 | GrowBuffer(requiredCapacity); 75 | } 76 | 77 | strcat(m_buffer, txt); 78 | return *this; 79 | } 80 | 81 | MemoryOutStream& MemoryOutStream::operator << (int const n) 82 | { 83 | FormatToStream(*this, "%i", n); 84 | return *this; 85 | } 86 | 87 | MemoryOutStream& MemoryOutStream::operator << (long const n) 88 | { 89 | FormatToStream(*this, "%li", n); 90 | return *this; 91 | } 92 | 93 | MemoryOutStream& MemoryOutStream::operator << (unsigned long const n) 94 | { 95 | FormatToStream(*this, "%lu", n); 96 | return *this; 97 | } 98 | 99 | MemoryOutStream& MemoryOutStream::operator << (float const f) 100 | { 101 | FormatToStream(*this, "%ff", f); 102 | return *this; 103 | } 104 | 105 | MemoryOutStream& MemoryOutStream::operator << (void const* p) 106 | { 107 | FormatToStream(*this, "%p", p); 108 | return *this; 109 | } 110 | 111 | MemoryOutStream& MemoryOutStream::operator << (unsigned int const s) 112 | { 113 | FormatToStream(*this, "%u", s); 114 | return *this; 115 | } 116 | 117 | MemoryOutStream& MemoryOutStream::operator <<(double const d) 118 | { 119 | FormatToStream(*this, "%f", d); 120 | return *this; 121 | } 122 | 123 | int MemoryOutStream::GetCapacity() const 124 | { 125 | return m_capacity; 126 | } 127 | 128 | 129 | void MemoryOutStream::GrowBuffer(int const desiredCapacity) 130 | { 131 | int const newCapacity = RoundUpToMultipleOfPow2Number(desiredCapacity, GROW_CHUNK_SIZE); 132 | 133 | using namespace std; 134 | 135 | char* buffer = new char[newCapacity]; 136 | if (m_buffer) 137 | strcpy(buffer, m_buffer); 138 | else 139 | strcpy(buffer, ""); 140 | 141 | delete [] m_buffer; 142 | m_buffer = buffer; 143 | m_capacity = newCapacity; 144 | } 145 | 146 | } 147 | 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /UnitTest++/src/MemoryOutStream.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_MEMORYOUTSTREAM_H 2 | #define UNITTEST_MEMORYOUTSTREAM_H 3 | 4 | #include "Config.h" 5 | 6 | #ifndef UNITTEST_USE_CUSTOM_STREAMS 7 | 8 | #include 9 | 10 | namespace UnitTest 11 | { 12 | 13 | class MemoryOutStream : public std::ostringstream 14 | { 15 | public: 16 | MemoryOutStream() {} 17 | char const* GetText() const; 18 | 19 | private: 20 | MemoryOutStream(MemoryOutStream const&); 21 | void operator =(MemoryOutStream const&); 22 | 23 | mutable std::string m_text; 24 | }; 25 | 26 | } 27 | 28 | #else 29 | 30 | #include 31 | 32 | namespace UnitTest 33 | { 34 | 35 | class MemoryOutStream 36 | { 37 | public: 38 | explicit MemoryOutStream(int const size = 256); 39 | ~MemoryOutStream(); 40 | 41 | char const* GetText() const; 42 | 43 | MemoryOutStream& operator << (char const* txt); 44 | MemoryOutStream& operator << (int n); 45 | MemoryOutStream& operator << (long n); 46 | MemoryOutStream& operator << (unsigned long n); 47 | MemoryOutStream& operator << (float f); 48 | MemoryOutStream& operator << (double d); 49 | MemoryOutStream& operator << (void const* p); 50 | MemoryOutStream& operator << (unsigned int s); 51 | 52 | enum { GROW_CHUNK_SIZE = 32 }; 53 | int GetCapacity() const; 54 | 55 | private: 56 | void operator= (MemoryOutStream const&); 57 | void GrowBuffer(int capacity); 58 | 59 | int m_capacity; 60 | char* m_buffer; 61 | }; 62 | 63 | } 64 | 65 | #endif 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /UnitTest++/src/Posix/SignalTranslator.cpp: -------------------------------------------------------------------------------- 1 | #include "SignalTranslator.h" 2 | 3 | namespace UnitTest { 4 | 5 | sigjmp_buf* SignalTranslator::s_jumpTarget = 0; 6 | 7 | namespace { 8 | 9 | void SignalHandler(int sig) 10 | { 11 | siglongjmp(*SignalTranslator::s_jumpTarget, sig ); 12 | } 13 | 14 | } 15 | 16 | 17 | SignalTranslator::SignalTranslator() 18 | { 19 | m_oldJumpTarget = s_jumpTarget; 20 | s_jumpTarget = &m_currentJumpTarget; 21 | 22 | struct sigaction action; 23 | action.sa_flags = 0; 24 | action.sa_handler = SignalHandler; 25 | sigemptyset( &action.sa_mask ); 26 | 27 | sigaction( SIGSEGV, &action, &m_old_SIGSEGV_action ); 28 | sigaction( SIGFPE , &action, &m_old_SIGFPE_action ); 29 | sigaction( SIGTRAP, &action, &m_old_SIGTRAP_action ); 30 | sigaction( SIGBUS , &action, &m_old_SIGBUS_action ); 31 | sigaction( SIGILL , &action, &m_old_SIGBUS_action ); 32 | } 33 | 34 | SignalTranslator::~SignalTranslator() 35 | { 36 | sigaction( SIGILL , &m_old_SIGBUS_action , 0 ); 37 | sigaction( SIGBUS , &m_old_SIGBUS_action , 0 ); 38 | sigaction( SIGTRAP, &m_old_SIGTRAP_action, 0 ); 39 | sigaction( SIGFPE , &m_old_SIGFPE_action , 0 ); 40 | sigaction( SIGSEGV, &m_old_SIGSEGV_action, 0 ); 41 | 42 | s_jumpTarget = m_oldJumpTarget; 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /UnitTest++/src/Posix/SignalTranslator.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_SIGNALTRANSLATOR_H 2 | #define UNITTEST_SIGNALTRANSLATOR_H 3 | 4 | #include 5 | #include 6 | 7 | namespace UnitTest { 8 | 9 | class SignalTranslator 10 | { 11 | public: 12 | SignalTranslator(); 13 | ~SignalTranslator(); 14 | 15 | static sigjmp_buf* s_jumpTarget; 16 | 17 | private: 18 | sigjmp_buf m_currentJumpTarget; 19 | sigjmp_buf* m_oldJumpTarget; 20 | 21 | struct sigaction m_old_SIGFPE_action; 22 | struct sigaction m_old_SIGTRAP_action; 23 | struct sigaction m_old_SIGSEGV_action; 24 | struct sigaction m_old_SIGBUS_action; 25 | struct sigaction m_old_SIGABRT_action; 26 | struct sigaction m_old_SIGALRM_action; 27 | }; 28 | 29 | #if !defined (__GNUC__) 30 | #define UNITTEST_EXTENSION 31 | #else 32 | #define UNITTEST_EXTENSION __extension__ 33 | #endif 34 | 35 | #define UNITTEST_THROW_SIGNALS \ 36 | UnitTest::SignalTranslator sig; \ 37 | if (UNITTEST_EXTENSION sigsetjmp(*UnitTest::SignalTranslator::s_jumpTarget, 1) != 0) \ 38 | throw ("Unhandled system exception"); 39 | 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /UnitTest++/src/Posix/TimeHelpers.cpp: -------------------------------------------------------------------------------- 1 | #include "TimeHelpers.h" 2 | #include 3 | 4 | namespace UnitTest { 5 | 6 | Timer::Timer() 7 | { 8 | m_startTime.tv_sec = 0; 9 | m_startTime.tv_usec = 0; 10 | } 11 | 12 | void Timer::Start() 13 | { 14 | gettimeofday(&m_startTime, 0); 15 | } 16 | 17 | 18 | int Timer::GetTimeInMs() const 19 | { 20 | struct timeval currentTime; 21 | gettimeofday(¤tTime, 0); 22 | int const dsecs = currentTime.tv_sec - m_startTime.tv_sec; 23 | int const dus = currentTime.tv_usec - m_startTime.tv_usec; 24 | return dsecs*1000 + dus/1000; 25 | } 26 | 27 | 28 | void TimeHelpers::SleepMs (int ms) 29 | { 30 | usleep(ms * 1000); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /UnitTest++/src/Posix/TimeHelpers.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TIMEHELPERS_H 2 | #define UNITTEST_TIMEHELPERS_H 3 | 4 | #include 5 | 6 | namespace UnitTest { 7 | 8 | class Timer 9 | { 10 | public: 11 | Timer(); 12 | void Start(); 13 | int GetTimeInMs() const; 14 | 15 | private: 16 | struct timeval m_startTime; 17 | }; 18 | 19 | 20 | namespace TimeHelpers 21 | { 22 | void SleepMs (int ms); 23 | } 24 | 25 | 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /UnitTest++/src/ReportAssert.cpp: -------------------------------------------------------------------------------- 1 | #include "AssertException.h" 2 | 3 | namespace UnitTest { 4 | 5 | void ReportAssert(char const* description, char const* filename, int lineNumber) 6 | { 7 | throw AssertException(description, filename, lineNumber); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /UnitTest++/src/ReportAssert.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_ASSERT_H 2 | #define UNITTEST_ASSERT_H 3 | 4 | namespace UnitTest { 5 | 6 | void ReportAssert(char const* description, char const* filename, int lineNumber); 7 | 8 | } 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /UnitTest++/src/Test.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | #include "Test.h" 3 | #include "TestList.h" 4 | #include "TestResults.h" 5 | #include "AssertException.h" 6 | #include "MemoryOutStream.h" 7 | #include "ExecuteTest.h" 8 | 9 | #ifdef UNITTEST_POSIX 10 | #include "Posix/SignalTranslator.h" 11 | #endif 12 | 13 | namespace UnitTest { 14 | 15 | TestList& Test::GetTestList() 16 | { 17 | static TestList s_list; 18 | return s_list; 19 | } 20 | 21 | Test::Test(char const* testName, char const* suiteName, char const* filename, int lineNumber) 22 | : m_details(testName, suiteName, filename, lineNumber) 23 | , next(0) 24 | , m_timeConstraintExempt(false) 25 | { 26 | } 27 | 28 | Test::~Test() 29 | { 30 | } 31 | 32 | void Test::Run() 33 | { 34 | ExecuteTest(*this, m_details); 35 | } 36 | 37 | void Test::RunImpl() const 38 | { 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /UnitTest++/src/Test.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TEST_H 2 | #define UNITTEST_TEST_H 3 | 4 | #include "TestDetails.h" 5 | 6 | namespace UnitTest { 7 | 8 | class TestResults; 9 | class TestList; 10 | 11 | class Test 12 | { 13 | public: 14 | explicit Test(char const* testName, char const* suiteName = "DefaultSuite", char const* filename = "", int lineNumber = 0); 15 | virtual ~Test(); 16 | void Run(); 17 | 18 | TestDetails const m_details; 19 | Test* next; 20 | mutable bool m_timeConstraintExempt; 21 | 22 | static TestList& GetTestList(); 23 | 24 | virtual void RunImpl() const; 25 | 26 | private: 27 | Test(Test const&); 28 | Test& operator =(Test const&); 29 | }; 30 | 31 | 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /UnitTest++/src/TestDetails.cpp: -------------------------------------------------------------------------------- 1 | #include "TestDetails.h" 2 | 3 | namespace UnitTest { 4 | 5 | TestDetails::TestDetails(char const* testName_, char const* suiteName_, char const* filename_, int lineNumber_) 6 | : suiteName(suiteName_) 7 | , testName(testName_) 8 | , filename(filename_) 9 | , lineNumber(lineNumber_) 10 | { 11 | } 12 | 13 | TestDetails::TestDetails(const TestDetails& details, int lineNumber_) 14 | : suiteName(details.suiteName) 15 | , testName(details.testName) 16 | , filename(details.filename) 17 | , lineNumber(lineNumber_) 18 | { 19 | } 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /UnitTest++/src/TestDetails.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTDETAILS_H 2 | #define UNITTEST_TESTDETAILS_H 3 | 4 | namespace UnitTest { 5 | 6 | class TestDetails 7 | { 8 | public: 9 | TestDetails(char const* testName, char const* suiteName, char const* filename, int lineNumber); 10 | TestDetails(const TestDetails& details, int lineNumber); 11 | 12 | char const* const suiteName; 13 | char const* const testName; 14 | char const* const filename; 15 | int const lineNumber; 16 | 17 | TestDetails(TestDetails const&); // Why is it public? --> http://gcc.gnu.org/bugs.html#cxx_rvalbind 18 | private: 19 | TestDetails& operator=(TestDetails const&); 20 | }; 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /UnitTest++/src/TestList.cpp: -------------------------------------------------------------------------------- 1 | #include "TestList.h" 2 | #include "Test.h" 3 | 4 | #include 5 | 6 | namespace UnitTest { 7 | 8 | TestList::TestList() 9 | : m_head(0) 10 | , m_tail(0) 11 | { 12 | } 13 | 14 | void TestList::Add(Test* test) 15 | { 16 | if (m_tail == 0) 17 | { 18 | assert(m_head == 0); 19 | m_head = test; 20 | m_tail = test; 21 | } 22 | else 23 | { 24 | m_tail->next = test; 25 | m_tail = test; 26 | } 27 | } 28 | 29 | Test* TestList::GetHead() const 30 | { 31 | return m_head; 32 | } 33 | 34 | ListAdder::ListAdder(TestList& list, Test* test) 35 | { 36 | list.Add(test); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /UnitTest++/src/TestList.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTLIST_H 2 | #define UNITTEST_TESTLIST_H 3 | 4 | 5 | namespace UnitTest { 6 | 7 | class Test; 8 | 9 | class TestList 10 | { 11 | public: 12 | TestList(); 13 | void Add (Test* test); 14 | 15 | Test* GetHead() const; 16 | 17 | private: 18 | Test* m_head; 19 | Test* m_tail; 20 | }; 21 | 22 | 23 | class ListAdder 24 | { 25 | public: 26 | ListAdder(TestList& list, Test* test); 27 | }; 28 | 29 | } 30 | 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /UnitTest++/src/TestMacros.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTMACROS_H 2 | #define UNITTEST_TESTMACROS_H 3 | 4 | #include "Config.h" 5 | #include "ExecuteTest.h" 6 | #include "AssertException.h" 7 | #include "TestDetails.h" 8 | #include "MemoryOutStream.h" 9 | 10 | #ifndef UNITTEST_POSIX 11 | #define UNITTEST_THROW_SIGNALS 12 | #else 13 | #include "Posix/SignalTranslator.h" 14 | #endif 15 | 16 | #ifdef TEST 17 | #error UnitTest++ redefines TEST 18 | #endif 19 | 20 | #ifdef TEST_EX 21 | #error UnitTest++ redefines TEST_EX 22 | #endif 23 | 24 | #ifdef TEST_FIXTURE_EX 25 | #error UnitTest++ redefines TEST_FIXTURE_EX 26 | #endif 27 | 28 | #define SUITE(Name) \ 29 | namespace Suite##Name { \ 30 | namespace UnitTestSuite { \ 31 | inline char const* GetSuiteName () { \ 32 | return #Name ; \ 33 | } \ 34 | } \ 35 | } \ 36 | namespace Suite##Name 37 | 38 | #define TEST_EX(Name, List) \ 39 | class Test##Name : public UnitTest::Test \ 40 | { \ 41 | public: \ 42 | Test##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {} \ 43 | private: \ 44 | virtual void RunImpl() const; \ 45 | } test##Name##Instance; \ 46 | \ 47 | UnitTest::ListAdder adder##Name (List, &test##Name##Instance); \ 48 | \ 49 | void Test##Name::RunImpl() const 50 | 51 | 52 | #define TEST(Name) TEST_EX(Name, UnitTest::Test::GetTestList()) 53 | 54 | 55 | #define TEST_FIXTURE_EX(Fixture, Name, List) \ 56 | class Fixture##Name##Helper : public Fixture \ 57 | { \ 58 | public: \ 59 | explicit Fixture##Name##Helper(UnitTest::TestDetails const& details) : m_details(details) {} \ 60 | void RunImpl(); \ 61 | UnitTest::TestDetails const& m_details; \ 62 | private: \ 63 | Fixture##Name##Helper(Fixture##Name##Helper const&); \ 64 | Fixture##Name##Helper& operator =(Fixture##Name##Helper const&); \ 65 | }; \ 66 | \ 67 | class Test##Fixture##Name : public UnitTest::Test \ 68 | { \ 69 | public: \ 70 | Test##Fixture##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {} \ 71 | private: \ 72 | virtual void RunImpl() const; \ 73 | } test##Fixture##Name##Instance; \ 74 | \ 75 | UnitTest::ListAdder adder##Fixture##Name (List, &test##Fixture##Name##Instance); \ 76 | \ 77 | void Test##Fixture##Name::RunImpl() const \ 78 | { \ 79 | bool ctorOk = false; \ 80 | try { \ 81 | Fixture##Name##Helper fixtureHelper(m_details); \ 82 | ctorOk = true; \ 83 | UnitTest::ExecuteTest(fixtureHelper, m_details); \ 84 | } \ 85 | catch (UnitTest::AssertException const& e) \ 86 | { \ 87 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(m_details.testName, m_details.suiteName, e.Filename(), e.LineNumber()), e.what()); \ 88 | } \ 89 | catch (std::exception const& e) \ 90 | { \ 91 | UnitTest::MemoryOutStream stream; \ 92 | stream << "Unhandled exception: " << e.what(); \ 93 | UnitTest::CurrentTest::Results()->OnTestFailure(m_details, stream.GetText()); \ 94 | } \ 95 | catch (...) { \ 96 | if (ctorOk) \ 97 | { \ 98 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(m_details, __LINE__), \ 99 | "Unhandled exception while destroying fixture " #Fixture); \ 100 | } \ 101 | else \ 102 | { \ 103 | UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(m_details, __LINE__), \ 104 | "Unhandled exception while constructing fixture " #Fixture); \ 105 | } \ 106 | } \ 107 | } \ 108 | void Fixture##Name##Helper::RunImpl() 109 | 110 | #define TEST_FIXTURE(Fixture,Name) TEST_FIXTURE_EX(Fixture, Name, UnitTest::Test::GetTestList()) 111 | 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /UnitTest++/src/TestReporter.cpp: -------------------------------------------------------------------------------- 1 | #include "TestReporter.h" 2 | 3 | namespace UnitTest { 4 | 5 | 6 | TestReporter::~TestReporter() 7 | { 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /UnitTest++/src/TestReporter.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTREPORTER_H 2 | #define UNITTEST_TESTREPORTER_H 3 | 4 | namespace UnitTest { 5 | 6 | class TestDetails; 7 | 8 | class TestReporter 9 | { 10 | public: 11 | virtual ~TestReporter(); 12 | 13 | virtual void ReportTestStart(TestDetails const& test) = 0; 14 | virtual void ReportFailure(TestDetails const& test, char const* failure) = 0; 15 | virtual void ReportTestFinish(TestDetails const& test, float secondsElapsed) = 0; 16 | virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed) = 0; 17 | }; 18 | 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /UnitTest++/src/TestReporterStdout.cpp: -------------------------------------------------------------------------------- 1 | #include "TestReporterStdout.h" 2 | #include 3 | 4 | #include "TestDetails.h" 5 | 6 | namespace UnitTest { 7 | 8 | void TestReporterStdout::ReportFailure(TestDetails const& details, char const* failure) 9 | { 10 | #if defined(__APPLE__) || defined(__GNUG__) 11 | char const* const errorFormat = "%s:%d: error: Failure in %s: %s\n"; 12 | #else 13 | char const* const errorFormat = "%s(%d): error: Failure in %s: %s\n"; 14 | #endif 15 | 16 | using namespace std; 17 | printf(errorFormat, details.filename, details.lineNumber, details.testName, failure); 18 | } 19 | 20 | void TestReporterStdout::ReportTestStart(TestDetails const& /*test*/) 21 | { 22 | } 23 | 24 | void TestReporterStdout::ReportTestFinish(TestDetails const& /*test*/, float) 25 | { 26 | } 27 | 28 | void TestReporterStdout::ReportSummary(int const totalTestCount, int const failedTestCount, 29 | int const failureCount, float secondsElapsed) 30 | { 31 | using namespace std; 32 | 33 | if (failureCount > 0) 34 | printf("FAILURE: %d out of %d tests failed (%d failures).\n", failedTestCount, totalTestCount, failureCount); 35 | else 36 | printf("Success: %d tests passed.\n", totalTestCount); 37 | 38 | printf("Test time: %.2f seconds.\n", secondsElapsed); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /UnitTest++/src/TestReporterStdout.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTREPORTERSTDOUT_H 2 | #define UNITTEST_TESTREPORTERSTDOUT_H 3 | 4 | #include "TestReporter.h" 5 | 6 | namespace UnitTest { 7 | 8 | class TestReporterStdout : public TestReporter 9 | { 10 | private: 11 | virtual void ReportTestStart(TestDetails const& test); 12 | virtual void ReportFailure(TestDetails const& test, char const* failure); 13 | virtual void ReportTestFinish(TestDetails const& test, float secondsElapsed); 14 | virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed); 15 | }; 16 | 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /UnitTest++/src/TestResults.cpp: -------------------------------------------------------------------------------- 1 | #include "TestResults.h" 2 | #include "TestReporter.h" 3 | 4 | #include "TestDetails.h" 5 | 6 | namespace UnitTest { 7 | 8 | TestResults::TestResults(TestReporter* testReporter) 9 | : m_testReporter(testReporter) 10 | , m_totalTestCount(0) 11 | , m_failedTestCount(0) 12 | , m_failureCount(0) 13 | , m_currentTestFailed(false) 14 | { 15 | } 16 | 17 | void TestResults::OnTestStart(TestDetails const& test) 18 | { 19 | ++m_totalTestCount; 20 | m_currentTestFailed = false; 21 | if (m_testReporter) 22 | m_testReporter->ReportTestStart(test); 23 | } 24 | 25 | void TestResults::OnTestFailure(TestDetails const& test, char const* failure) 26 | { 27 | ++m_failureCount; 28 | if (!m_currentTestFailed) 29 | { 30 | ++m_failedTestCount; 31 | m_currentTestFailed = true; 32 | } 33 | 34 | if (m_testReporter) 35 | m_testReporter->ReportFailure(test, failure); 36 | } 37 | 38 | void TestResults::OnTestFinish(TestDetails const& test, float secondsElapsed) 39 | { 40 | if (m_testReporter) 41 | m_testReporter->ReportTestFinish(test, secondsElapsed); 42 | } 43 | 44 | int TestResults::GetTotalTestCount() const 45 | { 46 | return m_totalTestCount; 47 | } 48 | 49 | int TestResults::GetFailedTestCount() const 50 | { 51 | return m_failedTestCount; 52 | } 53 | 54 | int TestResults::GetFailureCount() const 55 | { 56 | return m_failureCount; 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /UnitTest++/src/TestResults.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTRESULTS_H 2 | #define UNITTEST_TESTRESULTS_H 3 | 4 | namespace UnitTest { 5 | 6 | class TestReporter; 7 | class TestDetails; 8 | 9 | class TestResults 10 | { 11 | public: 12 | explicit TestResults(TestReporter* reporter = 0); 13 | 14 | void OnTestStart(TestDetails const& test); 15 | void OnTestFailure(TestDetails const& test, char const* failure); 16 | void OnTestFinish(TestDetails const& test, float secondsElapsed); 17 | 18 | int GetTotalTestCount() const; 19 | int GetFailedTestCount() const; 20 | int GetFailureCount() const; 21 | 22 | private: 23 | TestReporter* m_testReporter; 24 | int m_totalTestCount; 25 | int m_failedTestCount; 26 | int m_failureCount; 27 | 28 | bool m_currentTestFailed; 29 | 30 | TestResults(TestResults const&); 31 | TestResults& operator =(TestResults const&); 32 | }; 33 | 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /UnitTest++/src/TestRunner.cpp: -------------------------------------------------------------------------------- 1 | #include "TestRunner.h" 2 | #include "TestResults.h" 3 | #include "TestReporter.h" 4 | #include "TestReporterStdout.h" 5 | #include "TimeHelpers.h" 6 | #include "MemoryOutStream.h" 7 | 8 | #include 9 | 10 | 11 | namespace UnitTest { 12 | 13 | int RunAllTests() 14 | { 15 | TestReporterStdout reporter; 16 | TestRunner runner(reporter); 17 | return runner.RunTestsIf(Test::GetTestList(), NULL, True(), 0); 18 | } 19 | 20 | 21 | TestRunner::TestRunner(TestReporter& reporter) 22 | : m_reporter(&reporter) 23 | , m_result(new TestResults(&reporter)) 24 | , m_timer(new Timer) 25 | { 26 | m_timer->Start(); 27 | } 28 | 29 | TestRunner::~TestRunner() 30 | { 31 | delete m_result; 32 | delete m_timer; 33 | } 34 | 35 | int TestRunner::Finish() const 36 | { 37 | float const secondsElapsed = m_timer->GetTimeInMs() / 1000.0f; 38 | m_reporter->ReportSummary(m_result->GetTotalTestCount(), 39 | m_result->GetFailedTestCount(), 40 | m_result->GetFailureCount(), 41 | secondsElapsed); 42 | 43 | return m_result->GetFailureCount(); 44 | } 45 | 46 | bool TestRunner::IsTestInSuite(const Test* const curTest, char const* suiteName) const 47 | { 48 | using namespace std; 49 | return (suiteName == NULL) || !strcmp(curTest->m_details.suiteName, suiteName); 50 | } 51 | 52 | void TestRunner::RunTest(TestResults* const result, Test* const curTest, int const maxTestTimeInMs) const 53 | { 54 | CurrentTest::Results() = result; 55 | 56 | Timer testTimer; 57 | testTimer.Start(); 58 | 59 | result->OnTestStart(curTest->m_details); 60 | 61 | curTest->Run(); 62 | 63 | int const testTimeInMs = testTimer.GetTimeInMs(); 64 | if (maxTestTimeInMs > 0 && testTimeInMs > maxTestTimeInMs && !curTest->m_timeConstraintExempt) 65 | { 66 | MemoryOutStream stream; 67 | stream << "Global time constraint failed. Expected under " << maxTestTimeInMs << 68 | "ms but took " << testTimeInMs << "ms."; 69 | 70 | result->OnTestFailure(curTest->m_details, stream.GetText()); 71 | } 72 | 73 | result->OnTestFinish(curTest->m_details, testTimeInMs/1000.0f); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /UnitTest++/src/TestRunner.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTRUNNER_H 2 | #define UNITTEST_TESTRUNNER_H 3 | 4 | #include "Test.h" 5 | #include "TestList.h" 6 | #include "CurrentTest.h" 7 | 8 | namespace UnitTest { 9 | 10 | class TestReporter; 11 | class TestResults; 12 | class Timer; 13 | 14 | int RunAllTests(); 15 | 16 | struct True 17 | { 18 | bool operator()(const Test* const) const 19 | { 20 | return true; 21 | } 22 | }; 23 | 24 | class TestRunner 25 | { 26 | public: 27 | explicit TestRunner(TestReporter& reporter); 28 | ~TestRunner(); 29 | 30 | template 31 | int RunTestsIf(TestList const& list, char const* suiteName, 32 | const Predicate& predicate, int maxTestTimeInMs) const 33 | { 34 | Test* curTest = list.GetHead(); 35 | 36 | while (curTest != 0) 37 | { 38 | if (IsTestInSuite(curTest,suiteName) && predicate(curTest)) 39 | { 40 | RunTest(m_result, curTest, maxTestTimeInMs); 41 | } 42 | 43 | curTest = curTest->next; 44 | } 45 | 46 | return Finish(); 47 | } 48 | 49 | private: 50 | TestReporter* m_reporter; 51 | TestResults* m_result; 52 | Timer* m_timer; 53 | 54 | int Finish() const; 55 | bool IsTestInSuite(const Test* const curTest, char const* suiteName) const; 56 | void RunTest(TestResults* const result, Test* const curTest, int const maxTestTimeInMs) const; 57 | }; 58 | 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /UnitTest++/src/TestSuite.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TESTSUITE_H 2 | #define UNITTEST_TESTSUITE_H 3 | 4 | namespace UnitTestSuite { 5 | 6 | inline char const* GetSuiteName () 7 | { 8 | return "DefaultSuite"; 9 | } 10 | 11 | } 12 | 13 | #endif 14 | 15 | -------------------------------------------------------------------------------- /UnitTest++/src/TimeConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "TimeConstraint.h" 2 | #include "TestResults.h" 3 | #include "MemoryOutStream.h" 4 | #include "CurrentTest.h" 5 | 6 | namespace UnitTest { 7 | 8 | 9 | TimeConstraint::TimeConstraint(int ms, TestDetails const& details) 10 | : m_details(details) 11 | , m_maxMs(ms) 12 | { 13 | m_timer.Start(); 14 | } 15 | 16 | TimeConstraint::~TimeConstraint() 17 | { 18 | int const totalTimeInMs = m_timer.GetTimeInMs(); 19 | if (totalTimeInMs > m_maxMs) 20 | { 21 | MemoryOutStream stream; 22 | stream << "Time constraint failed. Expected to run test under " << m_maxMs << 23 | "ms but took " << totalTimeInMs << "ms."; 24 | 25 | UnitTest::CurrentTest::Results()->OnTestFailure(m_details, stream.GetText()); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /UnitTest++/src/TimeConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TIMECONSTRAINT_H 2 | #define UNITTEST_TIMECONSTRAINT_H 3 | 4 | #include "TimeHelpers.h" 5 | 6 | namespace UnitTest { 7 | 8 | class TestResults; 9 | class TestDetails; 10 | 11 | class TimeConstraint 12 | { 13 | public: 14 | TimeConstraint(int ms, TestDetails const& details); 15 | ~TimeConstraint(); 16 | 17 | private: 18 | void operator=(TimeConstraint const&); 19 | TimeConstraint(TimeConstraint const&); 20 | 21 | Timer m_timer; 22 | TestDetails const& m_details; 23 | int const m_maxMs; 24 | }; 25 | 26 | #define UNITTEST_TIME_CONSTRAINT(ms) \ 27 | UnitTest::TimeConstraint unitTest__timeConstraint__(ms, UnitTest::TestDetails(m_details, __LINE__)) 28 | 29 | #define UNITTEST_TIME_CONSTRAINT_EXEMPT() do { m_timeConstraintExempt = true; } while (0) 30 | 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /UnitTest++/src/TimeHelpers.h: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | 3 | #if defined UNITTEST_POSIX 4 | #include "Posix/TimeHelpers.h" 5 | #else 6 | #include "Win32/TimeHelpers.h" 7 | #endif 8 | -------------------------------------------------------------------------------- /UnitTest++/src/UnitTest++.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTESTCPP_H 2 | #define UNITTESTCPP_H 3 | 4 | //lint -esym(1509,*Fixture) 5 | 6 | #include "Config.h" 7 | #include "Test.h" 8 | #include "TestList.h" 9 | #include "TestSuite.h" 10 | #include "TestResults.h" 11 | 12 | #include "TestMacros.h" 13 | 14 | #include "CheckMacros.h" 15 | #include "TestRunner.h" 16 | #include "TimeConstraint.h" 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /UnitTest++/src/Win32/TimeHelpers.cpp: -------------------------------------------------------------------------------- 1 | #include "TimeHelpers.h" 2 | #include 3 | 4 | namespace UnitTest { 5 | 6 | Timer::Timer() 7 | : m_startTime(0) 8 | , m_threadHandle(::GetCurrentThread()) 9 | { 10 | #if defined(_MSC_VER) && (_MSC_VER == 1200) // VC6 doesn't have DWORD_PTR? 11 | typedef unsigned long DWORD_PTR; 12 | #endif 13 | 14 | DWORD_PTR systemMask; 15 | ::GetProcessAffinityMask(GetCurrentProcess(), &m_processAffinityMask, &systemMask); 16 | 17 | ::SetThreadAffinityMask(m_threadHandle, 1); 18 | ::QueryPerformanceFrequency(reinterpret_cast< LARGE_INTEGER* >(&m_frequency)); 19 | ::SetThreadAffinityMask(m_threadHandle, m_processAffinityMask); 20 | } 21 | 22 | void Timer::Start() 23 | { 24 | m_startTime = GetTime(); 25 | } 26 | 27 | int Timer::GetTimeInMs() const 28 | { 29 | __int64 const elapsedTime = GetTime() - m_startTime; 30 | double const seconds = double(elapsedTime) / double(m_frequency); 31 | return int(seconds * 1000.0f); 32 | } 33 | 34 | __int64 Timer::GetTime() const 35 | { 36 | LARGE_INTEGER curTime; 37 | ::SetThreadAffinityMask(m_threadHandle, 1); 38 | ::QueryPerformanceCounter(&curTime); 39 | ::SetThreadAffinityMask(m_threadHandle, m_processAffinityMask); 40 | return curTime.QuadPart; 41 | } 42 | 43 | 44 | 45 | void TimeHelpers::SleepMs(int const ms) 46 | { 47 | ::Sleep(ms); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /UnitTest++/src/Win32/TimeHelpers.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_TIMEHELPERS_H 2 | #define UNITTEST_TIMEHELPERS_H 3 | 4 | #include "../Config.h" 5 | 6 | 7 | #ifdef UNITTEST_MINGW 8 | #ifndef __int64 9 | #define __int64 long long 10 | #endif 11 | #endif 12 | 13 | namespace UnitTest { 14 | 15 | class Timer 16 | { 17 | public: 18 | Timer(); 19 | void Start(); 20 | int GetTimeInMs() const; 21 | 22 | private: 23 | __int64 GetTime() const; 24 | 25 | void* m_threadHandle; 26 | 27 | #if defined(_WIN64) 28 | unsigned __int64 m_processAffinityMask; 29 | #else 30 | unsigned long m_processAffinityMask; 31 | #endif 32 | 33 | __int64 m_startTime; 34 | __int64 m_frequency; 35 | }; 36 | 37 | 38 | namespace TimeHelpers 39 | { 40 | void SleepMs (int ms); 41 | } 42 | 43 | 44 | } 45 | 46 | 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /UnitTest++/src/XmlTestReporter.cpp: -------------------------------------------------------------------------------- 1 | #include "XmlTestReporter.h" 2 | #include "Config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using std::string; 9 | using std::ostringstream; 10 | using std::ostream; 11 | 12 | namespace { 13 | 14 | void ReplaceChar(string& str, char c, string const& replacement) 15 | { 16 | for (size_t pos = str.find(c); pos != string::npos; pos = str.find(c, pos + 1)) 17 | str.replace(pos, 1, replacement); 18 | } 19 | 20 | string XmlEscape(string const& value) 21 | { 22 | string escaped = value; 23 | 24 | ReplaceChar(escaped, '&', "&"); 25 | ReplaceChar(escaped, '<', "<"); 26 | ReplaceChar(escaped, '>', ">"); 27 | ReplaceChar(escaped, '\'', "'"); 28 | ReplaceChar(escaped, '\"', """); 29 | 30 | return escaped; 31 | } 32 | 33 | string BuildFailureMessage(string const& file, int line, string const& message) 34 | { 35 | ostringstream failureMessage; 36 | failureMessage << file << "(" << line << ") : " << message; 37 | return failureMessage.str(); 38 | } 39 | 40 | } 41 | 42 | namespace UnitTest { 43 | 44 | XmlTestReporter::XmlTestReporter(ostream& ostream) 45 | : m_ostream(ostream) 46 | { 47 | } 48 | 49 | void XmlTestReporter::ReportSummary(int totalTestCount, int failedTestCount, 50 | int failureCount, float secondsElapsed) 51 | { 52 | AddXmlElement(m_ostream, NULL); 53 | 54 | BeginResults(m_ostream, totalTestCount, failedTestCount, failureCount, secondsElapsed); 55 | 56 | DeferredTestResultList const& results = GetResults(); 57 | for (DeferredTestResultList::const_iterator i = results.begin(); i != results.end(); ++i) 58 | { 59 | BeginTest(m_ostream, *i); 60 | 61 | if (i->failed) 62 | AddFailure(m_ostream, *i); 63 | 64 | EndTest(m_ostream, *i); 65 | } 66 | 67 | EndResults(m_ostream); 68 | } 69 | 70 | void XmlTestReporter::AddXmlElement(ostream& os, char const* encoding) 71 | { 72 | os << ""; 78 | } 79 | 80 | void XmlTestReporter::BeginResults(std::ostream& os, int totalTestCount, int failedTestCount, 81 | int failureCount, float secondsElapsed) 82 | { 83 | os << ""; 89 | } 90 | 91 | void XmlTestReporter::EndResults(std::ostream& os) 92 | { 93 | os << ""; 94 | } 95 | 96 | void XmlTestReporter::BeginTest(std::ostream& os, DeferredTestResult const& result) 97 | { 98 | os << ""; 108 | else 109 | os << "/>"; 110 | } 111 | 112 | void XmlTestReporter::AddFailure(std::ostream& os, DeferredTestResult const& result) 113 | { 114 | os << ">"; // close element 115 | 116 | for (DeferredTestResult::FailureVec::const_iterator it = result.failures.begin(); 117 | it != result.failures.end(); 118 | ++it) 119 | { 120 | string const escapedMessage = XmlEscape(it->second); 121 | string const message = BuildFailureMessage(result.failureFile, it->first, escapedMessage); 122 | 123 | os << ""; 124 | } 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /UnitTest++/src/XmlTestReporter.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_XMLTESTREPORTER_H 2 | #define UNITTEST_XMLTESTREPORTER_H 3 | 4 | #include "DeferredTestReporter.h" 5 | 6 | #include 7 | 8 | namespace UnitTest 9 | { 10 | 11 | class XmlTestReporter : public DeferredTestReporter 12 | { 13 | public: 14 | explicit XmlTestReporter(std::ostream& ostream); 15 | 16 | virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed); 17 | 18 | private: 19 | XmlTestReporter(XmlTestReporter const&); 20 | XmlTestReporter& operator=(XmlTestReporter const&); 21 | 22 | void AddXmlElement(std::ostream& os, char const* encoding); 23 | void BeginResults(std::ostream& os, int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed); 24 | void EndResults(std::ostream& os); 25 | void BeginTest(std::ostream& os, DeferredTestResult const& result); 26 | void AddFailure(std::ostream& os, DeferredTestResult const& result); 27 | void EndTest(std::ostream& os, DeferredTestResult const& result); 28 | 29 | std::ostream& m_ostream; 30 | }; 31 | 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../TestReporterStdout.h" 3 | 4 | 5 | int main(int, char const *[]) 6 | { 7 | return UnitTest::RunAllTests(); 8 | } 9 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/RecordingReporter.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_RECORDINGREPORTER_H 2 | #define UNITTEST_RECORDINGREPORTER_H 3 | 4 | #include "../TestReporter.h" 5 | #include 6 | 7 | #include "../TestDetails.h" 8 | 9 | struct RecordingReporter : public UnitTest::TestReporter 10 | { 11 | private: 12 | enum { kMaxStringLength = 256 }; 13 | 14 | public: 15 | RecordingReporter() 16 | : testRunCount(0) 17 | , testFailedCount(0) 18 | , lastFailedLine(0) 19 | , testFinishedCount(0) 20 | , lastFinishedTestTime(0) 21 | , summaryTotalTestCount(0) 22 | , summaryFailedTestCount(0) 23 | , summaryFailureCount(0) 24 | , summarySecondsElapsed(0) 25 | { 26 | lastStartedSuite[0] = '\0'; 27 | lastStartedTest[0] = '\0'; 28 | lastFailedFile[0] = '\0'; 29 | lastFailedSuite[0] = '\0'; 30 | lastFailedTest[0] = '\0'; 31 | lastFailedMessage[0] = '\0'; 32 | lastFinishedSuite[0] = '\0'; 33 | lastFinishedTest[0] = '\0'; 34 | } 35 | 36 | virtual void ReportTestStart(UnitTest::TestDetails const& test) 37 | { 38 | using namespace std; 39 | 40 | ++testRunCount; 41 | strcpy(lastStartedSuite, test.suiteName); 42 | strcpy(lastStartedTest, test.testName); 43 | } 44 | 45 | virtual void ReportFailure(UnitTest::TestDetails const& test, char const* failure) 46 | { 47 | using namespace std; 48 | 49 | ++testFailedCount; 50 | strcpy(lastFailedFile, test.filename); 51 | lastFailedLine = test.lineNumber; 52 | strcpy(lastFailedSuite, test.suiteName); 53 | strcpy(lastFailedTest, test.testName); 54 | strcpy(lastFailedMessage, failure); 55 | } 56 | 57 | virtual void ReportTestFinish(UnitTest::TestDetails const& test, float testDuration) 58 | { 59 | using namespace std; 60 | 61 | ++testFinishedCount; 62 | strcpy(lastFinishedSuite, test.suiteName); 63 | strcpy(lastFinishedTest, test.testName); 64 | lastFinishedTestTime = testDuration; 65 | } 66 | 67 | virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed) 68 | { 69 | summaryTotalTestCount = totalTestCount; 70 | summaryFailedTestCount = failedTestCount; 71 | summaryFailureCount = failureCount; 72 | summarySecondsElapsed = secondsElapsed; 73 | } 74 | 75 | int testRunCount; 76 | char lastStartedSuite[kMaxStringLength]; 77 | char lastStartedTest[kMaxStringLength]; 78 | 79 | int testFailedCount; 80 | char lastFailedFile[kMaxStringLength]; 81 | int lastFailedLine; 82 | char lastFailedSuite[kMaxStringLength]; 83 | char lastFailedTest[kMaxStringLength]; 84 | char lastFailedMessage[kMaxStringLength]; 85 | 86 | int testFinishedCount; 87 | char lastFinishedSuite[kMaxStringLength]; 88 | char lastFinishedTest[kMaxStringLength]; 89 | float lastFinishedTestTime; 90 | 91 | int summaryTotalTestCount; 92 | int summaryFailedTestCount; 93 | int summaryFailureCount; 94 | float summarySecondsElapsed; 95 | }; 96 | 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/ScopedCurrentTest.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_SCOPEDCURRENTTEST_H 2 | #define UNITTEST_SCOPEDCURRENTTEST_H 3 | 4 | #include "../CurrentTest.h" 5 | #include 6 | 7 | class ScopedCurrentTest 8 | { 9 | public: 10 | ScopedCurrentTest() 11 | : m_oldTestResults(UnitTest::CurrentTest::Results()) 12 | , m_oldTestDetails(UnitTest::CurrentTest::Details()) 13 | { 14 | } 15 | 16 | explicit ScopedCurrentTest(UnitTest::TestResults& newResults, const UnitTest::TestDetails* newDetails = NULL) 17 | : m_oldTestResults(UnitTest::CurrentTest::Results()) 18 | , m_oldTestDetails(UnitTest::CurrentTest::Details()) 19 | { 20 | UnitTest::CurrentTest::Results() = &newResults; 21 | 22 | if (newDetails != NULL) 23 | UnitTest::CurrentTest::Details() = newDetails; 24 | } 25 | 26 | ~ScopedCurrentTest() 27 | { 28 | UnitTest::CurrentTest::Results() = m_oldTestResults; 29 | UnitTest::CurrentTest::Details() = m_oldTestDetails; 30 | } 31 | 32 | private: 33 | UnitTest::TestResults* m_oldTestResults; 34 | const UnitTest::TestDetails* m_oldTestDetails; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestAssertHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../AssertException.h" 3 | #include "../ReportAssert.h" 4 | 5 | using namespace UnitTest; 6 | 7 | namespace { 8 | 9 | TEST(ReportAssertThrowsAssertException) 10 | { 11 | bool caught = false; 12 | 13 | try 14 | { 15 | ReportAssert("", "", 0); 16 | } 17 | catch(AssertException const&) 18 | { 19 | caught = true; 20 | } 21 | 22 | CHECK (true == caught); 23 | } 24 | 25 | TEST(ReportAssertSetsCorrectInfoInException) 26 | { 27 | const int lineNumber = 12345; 28 | const char* description = "description"; 29 | const char* filename = "filename"; 30 | 31 | try 32 | { 33 | ReportAssert(description, filename, lineNumber); 34 | } 35 | catch(AssertException const& e) 36 | { 37 | CHECK_EQUAL(description, e.what()); 38 | CHECK_EQUAL(filename, e.Filename()); 39 | CHECK_EQUAL(lineNumber, e.LineNumber()); 40 | } 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestCurrentTest.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../CurrentTest.h" 3 | #include "ScopedCurrentTest.h" 4 | 5 | namespace 6 | { 7 | 8 | TEST(CanSetandGetDetails) 9 | { 10 | bool ok = false; 11 | { 12 | ScopedCurrentTest scopedTest; 13 | 14 | const UnitTest::TestDetails* details = reinterpret_cast< const UnitTest::TestDetails* >(12345); 15 | UnitTest::CurrentTest::Details() = details; 16 | 17 | ok = (UnitTest::CurrentTest::Details() == details); 18 | } 19 | 20 | CHECK(ok); 21 | } 22 | 23 | TEST(CanSetAndGetResults) 24 | { 25 | bool ok = false; 26 | { 27 | ScopedCurrentTest scopedTest; 28 | 29 | UnitTest::TestResults results; 30 | UnitTest::CurrentTest::Results() = &results; 31 | 32 | ok = (UnitTest::CurrentTest::Results() == &results); 33 | } 34 | 35 | CHECK(ok); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestDeferredTestReporter.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../DeferredTestReporter.h" 3 | #include "../Config.h" 4 | #include 5 | 6 | namespace UnitTest 7 | { 8 | 9 | namespace 10 | { 11 | 12 | #ifdef UNITTEST_USE_CUSTOM_STREAMS 13 | MemoryOutStream& operator <<(MemoryOutStream& lhs, const std::string& rhs) 14 | { 15 | lhs << rhs.c_str(); 16 | return lhs; 17 | } 18 | #endif 19 | 20 | struct MockDeferredTestReporter : public DeferredTestReporter 21 | { 22 | virtual void ReportSummary(int, int, int, float) 23 | { 24 | } 25 | }; 26 | 27 | struct DeferredTestReporterFixture 28 | { 29 | DeferredTestReporterFixture() 30 | : testName("UniqueTestName") 31 | , testSuite("UniqueTestSuite") 32 | , fileName("filename.h") 33 | , lineNumber(12) 34 | , details(testName.c_str(), testSuite.c_str(), fileName.c_str(), lineNumber) 35 | { 36 | } 37 | 38 | MockDeferredTestReporter reporter; 39 | std::string const testName; 40 | std::string const testSuite; 41 | std::string const fileName; 42 | int const lineNumber; 43 | TestDetails const details; 44 | }; 45 | 46 | TEST_FIXTURE(DeferredTestReporterFixture, ReportTestStartCreatesANewDeferredTest) 47 | { 48 | reporter.ReportTestStart(details); 49 | CHECK_EQUAL(1, (int)reporter.GetResults().size()); 50 | } 51 | 52 | TEST_FIXTURE(DeferredTestReporterFixture, ReportTestStartCapturesTestNameAndSuite) 53 | { 54 | reporter.ReportTestStart(details); 55 | 56 | DeferredTestResult const& result = reporter.GetResults().at(0); 57 | CHECK_EQUAL(testName.c_str(), result.testName); 58 | CHECK_EQUAL(testSuite.c_str(), result.suiteName); 59 | } 60 | 61 | TEST_FIXTURE(DeferredTestReporterFixture, ReportTestEndCapturesTestTime) 62 | { 63 | float const elapsed = 123.45f; 64 | reporter.ReportTestStart(details); 65 | reporter.ReportTestFinish(details, elapsed); 66 | 67 | DeferredTestResult const& result = reporter.GetResults().at(0); 68 | CHECK_CLOSE(elapsed, result.timeElapsed, 0.0001f); 69 | } 70 | 71 | TEST_FIXTURE(DeferredTestReporterFixture, ReportFailureSavesFailureDetails) 72 | { 73 | char const* failure = "failure"; 74 | 75 | reporter.ReportTestStart(details); 76 | reporter.ReportFailure(details, failure); 77 | 78 | DeferredTestResult const& result = reporter.GetResults().at(0); 79 | CHECK(result.failed == true); 80 | CHECK_EQUAL(fileName.c_str(), result.failureFile); 81 | } 82 | 83 | TEST_FIXTURE(DeferredTestReporterFixture, ReportFailureSavesFailureDetailsForMultipleFailures) 84 | { 85 | char const* failure1 = "failure 1"; 86 | char const* failure2 = "failure 2"; 87 | 88 | reporter.ReportTestStart(details); 89 | reporter.ReportFailure(details, failure1); 90 | reporter.ReportFailure(details, failure2); 91 | 92 | DeferredTestResult const& result = reporter.GetResults().at(0); 93 | CHECK_EQUAL(2, (int)result.failures.size()); 94 | CHECK_EQUAL(failure1, result.failures[0].second); 95 | CHECK_EQUAL(failure2, result.failures[1].second); 96 | } 97 | 98 | TEST_FIXTURE(DeferredTestReporterFixture, DeferredTestReporterTakesCopyOfFailureMessage) 99 | { 100 | reporter.ReportTestStart(details); 101 | 102 | char failureMessage[128]; 103 | char const* goodStr = "Real failure message"; 104 | char const* badStr = "Bogus failure message"; 105 | 106 | using namespace std; 107 | 108 | strcpy(failureMessage, goodStr); 109 | reporter.ReportFailure(details, failureMessage); 110 | strcpy(failureMessage, badStr); 111 | 112 | DeferredTestResult const& result = reporter.GetResults().at(0); 113 | DeferredTestResult::Failure const& failure = result.failures.at(0); 114 | CHECK_EQUAL(goodStr, failure.second); 115 | } 116 | 117 | }} 118 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestMemoryOutStream.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | 3 | #include "../MemoryOutStream.h" 4 | #include 5 | 6 | using namespace UnitTest; 7 | using namespace std; 8 | 9 | namespace { 10 | 11 | TEST(DefaultIsEmptyString) 12 | { 13 | MemoryOutStream const stream; 14 | CHECK(stream.GetText() != 0); 15 | CHECK_EQUAL("", stream.GetText()); 16 | } 17 | 18 | TEST(StreamingTextCopiesCharacters) 19 | { 20 | MemoryOutStream stream; 21 | stream << "Lalala"; 22 | CHECK_EQUAL("Lalala", stream.GetText()); 23 | } 24 | 25 | TEST(StreamingMultipleTimesConcatenatesResult) 26 | { 27 | MemoryOutStream stream; 28 | stream << "Bork" << "Foo" << "Bar"; 29 | CHECK_EQUAL("BorkFooBar", stream.GetText()); 30 | } 31 | 32 | TEST(StreamingIntWritesCorrectCharacters) 33 | { 34 | MemoryOutStream stream; 35 | stream << (int)123; 36 | CHECK_EQUAL("123", stream.GetText()); 37 | } 38 | 39 | TEST(StreamingUnsignedIntWritesCorrectCharacters) 40 | { 41 | MemoryOutStream stream; 42 | stream << (unsigned int)123; 43 | CHECK_EQUAL("123", stream.GetText()); 44 | } 45 | 46 | TEST(StreamingLongWritesCorrectCharacters) 47 | { 48 | MemoryOutStream stream; 49 | stream << (long)(-123); 50 | CHECK_EQUAL("-123", stream.GetText()); 51 | } 52 | 53 | TEST(StreamingUnsignedLongWritesCorrectCharacters) 54 | { 55 | MemoryOutStream stream; 56 | stream << (unsigned long)123; 57 | CHECK_EQUAL("123", stream.GetText()); 58 | } 59 | 60 | TEST(StreamingFloatWritesCorrectCharacters) 61 | { 62 | MemoryOutStream stream; 63 | stream << 3.1415f; 64 | CHECK(strstr(stream.GetText(), "3.1415")); 65 | } 66 | 67 | TEST(StreamingDoubleWritesCorrectCharacters) 68 | { 69 | MemoryOutStream stream; 70 | stream << 3.1415; 71 | CHECK(strstr(stream.GetText(), "3.1415")); 72 | } 73 | 74 | TEST(StreamingPointerWritesCorrectCharacters) 75 | { 76 | MemoryOutStream stream; 77 | int* p = (int*)0x1234; 78 | stream << p; 79 | CHECK(strstr(stream.GetText(), "1234")); 80 | } 81 | 82 | TEST(StreamingSizeTWritesCorrectCharacters) 83 | { 84 | MemoryOutStream stream; 85 | size_t const s = 53124; 86 | stream << s; 87 | CHECK_EQUAL("53124", stream.GetText()); 88 | } 89 | 90 | #ifdef UNITTEST_USE_CUSTOM_STREAMS 91 | 92 | TEST(StreamInitialCapacityIsCorrect) 93 | { 94 | MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE); 95 | CHECK_EQUAL((int)MemoryOutStream::GROW_CHUNK_SIZE, stream.GetCapacity()); 96 | } 97 | 98 | TEST(StreamInitialCapacityIsMultipleOfGrowChunkSize) 99 | { 100 | MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE + 1); 101 | CHECK_EQUAL((int)MemoryOutStream::GROW_CHUNK_SIZE * 2, stream.GetCapacity()); 102 | } 103 | 104 | 105 | TEST(ExceedingCapacityGrowsBuffer) 106 | { 107 | MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE); 108 | stream << "012345678901234567890123456789"; 109 | char const* const oldBuffer = stream.GetText(); 110 | stream << "0123456789"; 111 | CHECK(oldBuffer != stream.GetText()); 112 | } 113 | 114 | TEST(ExceedingCapacityGrowsBufferByGrowChunk) 115 | { 116 | MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE); 117 | stream << "0123456789012345678901234567890123456789"; 118 | CHECK_EQUAL(MemoryOutStream::GROW_CHUNK_SIZE * 2, stream.GetCapacity()); 119 | } 120 | 121 | TEST(WritingStringLongerThanCapacityFitsInNewBuffer) 122 | { 123 | MemoryOutStream stream(8); 124 | stream << "0123456789ABCDEF"; 125 | CHECK_EQUAL("0123456789ABCDEF", stream.GetText()); 126 | } 127 | 128 | TEST(WritingIntLongerThanCapacityFitsInNewBuffer) 129 | { 130 | MemoryOutStream stream(8); 131 | stream << "aaaa" << 123456;; 132 | CHECK_EQUAL("aaaa123456", stream.GetText()); 133 | } 134 | 135 | TEST(WritingFloatLongerThanCapacityFitsInNewBuffer) 136 | { 137 | MemoryOutStream stream(8); 138 | stream << "aaaa" << 123456.0f;; 139 | CHECK_EQUAL("aaaa123456.000000f", stream.GetText()); 140 | } 141 | 142 | TEST(WritingSizeTLongerThanCapacityFitsInNewBuffer) 143 | { 144 | MemoryOutStream stream(8); 145 | stream << "aaaa" << size_t(32145); 146 | CHECK_EQUAL("aaaa32145", stream.GetText()); 147 | } 148 | 149 | #endif 150 | 151 | } 152 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestTest.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../TestReporter.h" 3 | #include "../TimeHelpers.h" 4 | #include "ScopedCurrentTest.h" 5 | 6 | using namespace UnitTest; 7 | 8 | namespace { 9 | 10 | TEST(PassingTestHasNoFailures) 11 | { 12 | class PassingTest : public Test 13 | { 14 | public: 15 | PassingTest() : Test("passing") {} 16 | virtual void RunImpl() const 17 | { 18 | CHECK(true); 19 | } 20 | }; 21 | 22 | TestResults results; 23 | { 24 | ScopedCurrentTest scopedResults(results); 25 | PassingTest().Run(); 26 | } 27 | 28 | CHECK_EQUAL(0, results.GetFailureCount()); 29 | } 30 | 31 | 32 | TEST(FailingTestHasFailures) 33 | { 34 | class FailingTest : public Test 35 | { 36 | public: 37 | FailingTest() : Test("failing") {} 38 | virtual void RunImpl() const 39 | { 40 | CHECK(false); 41 | } 42 | }; 43 | 44 | TestResults results; 45 | { 46 | ScopedCurrentTest scopedResults(results); 47 | FailingTest().Run(); 48 | } 49 | 50 | CHECK_EQUAL(1, results.GetFailureCount()); 51 | } 52 | 53 | 54 | TEST(ThrowingTestsAreReportedAsFailures) 55 | { 56 | class CrashingTest : public Test 57 | { 58 | public: 59 | CrashingTest() : Test("throwing") {} 60 | virtual void RunImpl() const 61 | { 62 | throw "Blah"; 63 | } 64 | }; 65 | 66 | TestResults results; 67 | { 68 | ScopedCurrentTest scopedResult(results); 69 | CrashingTest().Run(); 70 | } 71 | 72 | CHECK_EQUAL(1, results.GetFailureCount()); 73 | } 74 | 75 | 76 | #ifndef UNITTEST_MINGW 77 | TEST(CrashingTestsAreReportedAsFailures) 78 | { 79 | class CrashingTest : public Test 80 | { 81 | public: 82 | CrashingTest() : Test("crashing") {} 83 | virtual void RunImpl() const 84 | { 85 | reinterpret_cast< void (*)() >(0)(); 86 | } 87 | }; 88 | 89 | TestResults results; 90 | { 91 | ScopedCurrentTest scopedResult(results); 92 | CrashingTest().Run(); 93 | } 94 | 95 | CHECK_EQUAL(1, results.GetFailureCount()); 96 | } 97 | #endif 98 | 99 | TEST(TestWithUnspecifiedSuiteGetsDefaultSuite) 100 | { 101 | Test test("test"); 102 | CHECK(test.m_details.suiteName != NULL); 103 | CHECK_EQUAL("DefaultSuite", test.m_details.suiteName); 104 | } 105 | 106 | TEST(TestReflectsSpecifiedSuiteName) 107 | { 108 | Test test("test", "testSuite"); 109 | CHECK(test.m_details.suiteName != NULL); 110 | CHECK_EQUAL("testSuite", test.m_details.suiteName); 111 | } 112 | 113 | void Fail() 114 | { 115 | CHECK(false); 116 | } 117 | 118 | TEST(OutOfCoreCHECKMacrosCanFailTests) 119 | { 120 | TestResults results; 121 | { 122 | ScopedCurrentTest scopedResult(results); 123 | Fail(); 124 | } 125 | 126 | CHECK_EQUAL(1, results.GetFailureCount()); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestTestList.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../TestList.h" 3 | 4 | using namespace UnitTest; 5 | 6 | namespace { 7 | 8 | 9 | TEST (TestListIsEmptyByDefault) 10 | { 11 | TestList list; 12 | CHECK (list.GetHead() == 0); 13 | } 14 | 15 | TEST (AddingTestSetsHeadToTest) 16 | { 17 | Test test("test"); 18 | TestList list; 19 | list.Add(&test); 20 | 21 | CHECK (list.GetHead() == &test); 22 | CHECK (test.next == 0); 23 | } 24 | 25 | TEST (AddingSecondTestAddsItToEndOfList) 26 | { 27 | Test test1("test1"); 28 | Test test2("test2"); 29 | 30 | TestList list; 31 | list.Add(&test1); 32 | list.Add(&test2); 33 | 34 | CHECK (list.GetHead() == &test1); 35 | CHECK (test1.next == &test2); 36 | CHECK (test2.next == 0); 37 | } 38 | 39 | TEST (ListAdderAddsTestToList) 40 | { 41 | TestList list; 42 | 43 | Test test(""); 44 | ListAdder adder(list, &test); 45 | 46 | CHECK (list.GetHead() == &test); 47 | CHECK (test.next == 0); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestTestMacros.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../TestMacros.h" 3 | #include "../TestList.h" 4 | #include "../TestResults.h" 5 | #include "../TestReporter.h" 6 | #include "../ReportAssert.h" 7 | #include "RecordingReporter.h" 8 | #include "ScopedCurrentTest.h" 9 | 10 | using namespace UnitTest; 11 | 12 | namespace { 13 | 14 | TestList list1; 15 | TEST_EX(DummyTest, list1) 16 | { 17 | } 18 | 19 | TEST (TestsAreAddedToTheListThroughMacro) 20 | { 21 | CHECK(list1.GetHead() != 0); 22 | CHECK(list1.GetHead()->next == 0); 23 | } 24 | 25 | struct ThrowingThingie 26 | { 27 | ThrowingThingie() : dummy(false) 28 | { 29 | if (!dummy) 30 | throw "Oops"; 31 | } 32 | 33 | bool dummy; 34 | }; 35 | 36 | TestList list2; 37 | TEST_FIXTURE_EX(ThrowingThingie, DummyTestName, list2) 38 | { 39 | } 40 | 41 | TEST (ExceptionsInFixtureAreReportedAsHappeningInTheFixture) 42 | { 43 | RecordingReporter reporter; 44 | TestResults result(&reporter); 45 | { 46 | ScopedCurrentTest scopedResults(result); 47 | list2.GetHead()->Run(); 48 | } 49 | 50 | CHECK(strstr(reporter.lastFailedMessage, "xception")); 51 | CHECK(strstr(reporter.lastFailedMessage, "fixture")); 52 | CHECK(strstr(reporter.lastFailedMessage, "ThrowingThingie")); 53 | } 54 | 55 | struct DummyFixture 56 | { 57 | int x; 58 | }; 59 | 60 | // We're really testing the macros so we just want them to compile and link 61 | SUITE(TestSuite1) 62 | { 63 | TEST(SimilarlyNamedTestsInDifferentSuitesWork) 64 | { 65 | } 66 | 67 | TEST_FIXTURE(DummyFixture, SimilarlyNamedFixtureTestsInDifferentSuitesWork) 68 | { 69 | } 70 | } 71 | 72 | SUITE(TestSuite2) 73 | { 74 | TEST(SimilarlyNamedTestsInDifferentSuitesWork) 75 | { 76 | } 77 | 78 | TEST_FIXTURE(DummyFixture,SimilarlyNamedFixtureTestsInDifferentSuitesWork) 79 | { 80 | } 81 | } 82 | 83 | TestList macroTestList1; 84 | TEST_EX(MacroTestHelper1, macroTestList1) 85 | { 86 | } 87 | 88 | TEST(TestAddedWithTEST_EXMacroGetsDefaultSuite) 89 | { 90 | CHECK(macroTestList1.GetHead() != NULL); 91 | CHECK_EQUAL ("MacroTestHelper1", macroTestList1.GetHead()->m_details.testName); 92 | CHECK_EQUAL ("DefaultSuite", macroTestList1.GetHead()->m_details.suiteName); 93 | } 94 | 95 | TestList macroTestList2; 96 | TEST_FIXTURE_EX(DummyFixture, MacroTestHelper2, macroTestList2) 97 | { 98 | } 99 | 100 | TEST(TestAddedWithTEST_FIXTURE_EXMacroGetsDefaultSuite) 101 | { 102 | CHECK(macroTestList2.GetHead() != NULL); 103 | CHECK_EQUAL ("MacroTestHelper2", macroTestList2.GetHead()->m_details.testName); 104 | CHECK_EQUAL ("DefaultSuite", macroTestList2.GetHead()->m_details.suiteName); 105 | } 106 | 107 | struct FixtureCtorThrows 108 | { 109 | FixtureCtorThrows() { throw "exception"; } 110 | }; 111 | 112 | TestList throwingFixtureTestList1; 113 | TEST_FIXTURE_EX(FixtureCtorThrows, FixtureCtorThrowsTestName, throwingFixtureTestList1) 114 | { 115 | } 116 | 117 | TEST(FixturesWithThrowingCtorsAreFailures) 118 | { 119 | CHECK(throwingFixtureTestList1.GetHead() != NULL); 120 | RecordingReporter reporter; 121 | TestResults result(&reporter); 122 | { 123 | ScopedCurrentTest scopedResult(result); 124 | throwingFixtureTestList1.GetHead()->Run(); 125 | } 126 | 127 | int const failureCount = result.GetFailedTestCount(); 128 | CHECK_EQUAL(1, failureCount); 129 | CHECK(strstr(reporter.lastFailedMessage, "while constructing fixture")); 130 | } 131 | 132 | struct FixtureDtorThrows 133 | { 134 | ~FixtureDtorThrows() { throw "exception"; } 135 | }; 136 | 137 | TestList throwingFixtureTestList2; 138 | TEST_FIXTURE_EX(FixtureDtorThrows, FixtureDtorThrowsTestName, throwingFixtureTestList2) 139 | { 140 | } 141 | 142 | TEST(FixturesWithThrowingDtorsAreFailures) 143 | { 144 | CHECK(throwingFixtureTestList2.GetHead() != NULL); 145 | 146 | RecordingReporter reporter; 147 | TestResults result(&reporter); 148 | { 149 | ScopedCurrentTest scopedResult(result); 150 | throwingFixtureTestList2.GetHead()->Run(); 151 | } 152 | 153 | int const failureCount = result.GetFailedTestCount(); 154 | CHECK_EQUAL(1, failureCount); 155 | CHECK(strstr(reporter.lastFailedMessage, "while destroying fixture")); 156 | } 157 | 158 | const int FailingLine = 123; 159 | 160 | struct FixtureCtorAsserts 161 | { 162 | FixtureCtorAsserts() 163 | { 164 | UnitTest::ReportAssert("assert failure", "file", FailingLine); 165 | } 166 | }; 167 | 168 | TestList ctorAssertFixtureTestList; 169 | TEST_FIXTURE_EX(FixtureCtorAsserts, CorrectlyReportsAssertFailureInCtor, ctorAssertFixtureTestList) 170 | { 171 | } 172 | 173 | TEST(CorrectlyReportsFixturesWithCtorsThatAssert) 174 | { 175 | RecordingReporter reporter; 176 | TestResults result(&reporter); 177 | { 178 | ScopedCurrentTest scopedResults(result); 179 | ctorAssertFixtureTestList.GetHead()->Run(); 180 | } 181 | 182 | const int failureCount = result.GetFailedTestCount(); 183 | CHECK_EQUAL(1, failureCount); 184 | CHECK_EQUAL(FailingLine, reporter.lastFailedLine); 185 | CHECK(strstr(reporter.lastFailedMessage, "assert failure")); 186 | } 187 | 188 | } 189 | 190 | // We're really testing if it's possible to use the same suite in two files 191 | // to compile and link successfuly (TestTestSuite.cpp has suite with the same name) 192 | // Note: we are outside of the anonymous namespace 193 | SUITE(SameTestSuite) 194 | { 195 | TEST(DummyTest1) 196 | { 197 | } 198 | } 199 | 200 | #define CUR_TEST_NAME CurrentTestDetailsContainCurrentTestInfo 201 | #define INNER_STRINGIFY(X) #X 202 | #define STRINGIFY(X) INNER_STRINGIFY(X) 203 | 204 | TEST(CUR_TEST_NAME) 205 | { 206 | const UnitTest::TestDetails* details = CurrentTest::Details(); 207 | CHECK_EQUAL(STRINGIFY(CUR_TEST_NAME), details->testName); 208 | } 209 | 210 | #undef CUR_TEST_NAME 211 | #undef INNER_STRINGIFY 212 | #undef STRINGIFY 213 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestTestResults.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../TestResults.h" 3 | #include "RecordingReporter.h" 4 | 5 | using namespace UnitTest; 6 | 7 | namespace { 8 | 9 | TestDetails const details("testname", "suitename", "filename", 123); 10 | 11 | 12 | TEST(StartsWithNoTestsRun) 13 | { 14 | TestResults results; 15 | CHECK_EQUAL (0, results.GetTotalTestCount()); 16 | } 17 | 18 | TEST(RecordsNumbersOfTests) 19 | { 20 | TestResults results; 21 | results.OnTestStart(details); 22 | results.OnTestStart(details); 23 | results.OnTestStart(details); 24 | CHECK_EQUAL(3, results.GetTotalTestCount()); 25 | } 26 | 27 | TEST(StartsWithNoTestsFailing) 28 | { 29 | TestResults results; 30 | CHECK_EQUAL (0, results.GetFailureCount()); 31 | } 32 | 33 | TEST(RecordsNumberOfFailures) 34 | { 35 | TestResults results; 36 | results.OnTestFailure(details, ""); 37 | results.OnTestFailure(details, ""); 38 | CHECK_EQUAL(2, results.GetFailureCount()); 39 | } 40 | 41 | TEST(RecordsNumberOfFailedTests) 42 | { 43 | TestResults results; 44 | 45 | results.OnTestStart(details); 46 | results.OnTestFailure(details, ""); 47 | results.OnTestFinish(details, 0); 48 | 49 | results.OnTestStart(details); 50 | results.OnTestFailure(details, ""); 51 | results.OnTestFailure(details, ""); 52 | results.OnTestFailure(details, ""); 53 | results.OnTestFinish(details, 0); 54 | 55 | CHECK_EQUAL (2, results.GetFailedTestCount()); 56 | } 57 | 58 | TEST(NotifiesReporterOfTestStartWithCorrectInfo) 59 | { 60 | RecordingReporter reporter; 61 | TestResults results(&reporter); 62 | results.OnTestStart(details); 63 | 64 | CHECK_EQUAL (1, reporter.testRunCount); 65 | CHECK_EQUAL ("suitename", reporter.lastStartedSuite); 66 | CHECK_EQUAL ("testname", reporter.lastStartedTest); 67 | } 68 | 69 | TEST(NotifiesReporterOfTestFailureWithCorrectInfo) 70 | { 71 | RecordingReporter reporter; 72 | TestResults results(&reporter); 73 | 74 | results.OnTestFailure(details, "failurestring"); 75 | CHECK_EQUAL (1, reporter.testFailedCount); 76 | CHECK_EQUAL ("filename", reporter.lastFailedFile); 77 | CHECK_EQUAL (123, reporter.lastFailedLine); 78 | CHECK_EQUAL ("suitename", reporter.lastFailedSuite); 79 | CHECK_EQUAL ("testname", reporter.lastFailedTest); 80 | CHECK_EQUAL ("failurestring", reporter.lastFailedMessage); 81 | } 82 | 83 | TEST(NotifiesReporterOfCheckFailureWithCorrectInfo) 84 | { 85 | RecordingReporter reporter; 86 | TestResults results(&reporter); 87 | 88 | results.OnTestFailure(details, "failurestring"); 89 | CHECK_EQUAL (1, reporter.testFailedCount); 90 | 91 | CHECK_EQUAL ("filename", reporter.lastFailedFile); 92 | CHECK_EQUAL (123, reporter.lastFailedLine); 93 | CHECK_EQUAL ("testname", reporter.lastFailedTest); 94 | CHECK_EQUAL ("suitename", reporter.lastFailedSuite); 95 | CHECK_EQUAL ("failurestring", reporter.lastFailedMessage); 96 | } 97 | 98 | TEST(NotifiesReporterOfTestEnd) 99 | { 100 | RecordingReporter reporter; 101 | TestResults results(&reporter); 102 | 103 | results.OnTestFinish(details, 0.1234f); 104 | CHECK_EQUAL (1, reporter.testFinishedCount); 105 | CHECK_EQUAL ("testname", reporter.lastFinishedTest); 106 | CHECK_EQUAL ("suitename", reporter.lastFinishedSuite); 107 | CHECK_CLOSE (0.1234f, reporter.lastFinishedTestTime, 0.0001f); 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestTestSuite.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | 3 | // We're really testing if it's possible to use the same suite in two files 4 | // to compile and link successfuly (TestTestSuite.cpp has suite with the same name) 5 | // Note: we are outside of the anonymous namespace 6 | SUITE(SameTestSuite) 7 | { 8 | TEST(DummyTest2) 9 | { 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestTimeConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../TestResults.h" 3 | #include "../TimeHelpers.h" 4 | #include "RecordingReporter.h" 5 | #include "ScopedCurrentTest.h" 6 | 7 | using namespace UnitTest; 8 | 9 | namespace 10 | { 11 | 12 | TEST(TimeConstraintSucceedsWithFastTest) 13 | { 14 | TestResults result; 15 | { 16 | ScopedCurrentTest scopedResult(result); 17 | TimeConstraint t(200, TestDetails("", "", "", 0)); 18 | TimeHelpers::SleepMs(5); 19 | } 20 | CHECK_EQUAL(0, result.GetFailureCount()); 21 | } 22 | 23 | TEST(TimeConstraintFailsWithSlowTest) 24 | { 25 | TestResults result; 26 | { 27 | ScopedCurrentTest scopedResult(result); 28 | TimeConstraint t(10, TestDetails("", "", "", 0)); 29 | TimeHelpers::SleepMs(20); 30 | } 31 | CHECK_EQUAL(1, result.GetFailureCount()); 32 | } 33 | 34 | TEST(TimeConstraintFailureIncludesCorrectData) 35 | { 36 | RecordingReporter reporter; 37 | TestResults result(&reporter); 38 | { 39 | ScopedCurrentTest scopedResult(result); 40 | 41 | TestDetails const details("testname", "suitename", "filename", 10); 42 | TimeConstraint t(10, details); 43 | TimeHelpers::SleepMs(20); 44 | } 45 | 46 | using namespace std; 47 | 48 | CHECK(strstr(reporter.lastFailedFile, "filename")); 49 | CHECK_EQUAL(10, reporter.lastFailedLine); 50 | CHECK(strstr(reporter.lastFailedTest, "testname")); 51 | } 52 | 53 | TEST(TimeConstraintFailureIncludesTimeoutInformation) 54 | { 55 | RecordingReporter reporter; 56 | TestResults result(&reporter); 57 | { 58 | ScopedCurrentTest scopedResult(result); 59 | TimeConstraint t(10, TestDetails("", "", "", 0)); 60 | TimeHelpers::SleepMs(20); 61 | } 62 | 63 | using namespace std; 64 | 65 | CHECK(strstr(reporter.lastFailedMessage, "ime constraint")); 66 | CHECK(strstr(reporter.lastFailedMessage, "under 10ms")); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestTimeConstraintMacro.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../TimeHelpers.h" 3 | 4 | #include "RecordingReporter.h" 5 | #include "ScopedCurrentTest.h" 6 | 7 | namespace { 8 | 9 | TEST (TimeConstraintMacroQualifiesNamespace) 10 | { 11 | // If this compiles without a "using namespace UnitTest;", all is well. 12 | UNITTEST_TIME_CONSTRAINT(1); 13 | } 14 | 15 | TEST (TimeConstraintMacroUsesCorrectInfo) 16 | { 17 | int testLine = 0; 18 | RecordingReporter reporter; 19 | { 20 | UnitTest::TestResults testResults(&reporter); 21 | ScopedCurrentTest scopedResults(testResults); 22 | 23 | UNITTEST_TIME_CONSTRAINT(10); testLine = __LINE__; 24 | UnitTest::TimeHelpers::SleepMs(20); 25 | } 26 | 27 | using namespace std; 28 | 29 | CHECK_EQUAL(1, reporter.testFailedCount); 30 | CHECK(strstr(reporter.lastFailedFile, __FILE__)); 31 | CHECK_EQUAL(testLine, reporter.lastFailedLine); 32 | CHECK(strstr(reporter.lastFailedTest, "TimeConstraintMacroUsesCorrectInfo")); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestUnitTest++.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../ReportAssert.h" 3 | #include "ScopedCurrentTest.h" 4 | 5 | #include 6 | 7 | // These are sample tests that show the different features of the framework 8 | 9 | namespace { 10 | 11 | TEST(ValidCheckSucceeds) 12 | { 13 | bool const b = true; 14 | CHECK(b); 15 | } 16 | 17 | TEST(CheckWorksWithPointers) 18 | { 19 | void* p = (void *)0x100; 20 | CHECK(p); 21 | CHECK(p != 0); 22 | } 23 | 24 | TEST(ValidCheckEqualSucceeds) 25 | { 26 | int const x = 3; 27 | int const y = 3; 28 | CHECK_EQUAL(x, y); 29 | } 30 | 31 | TEST(CheckEqualWorksWithPointers) 32 | { 33 | void* p = (void *)0; 34 | CHECK_EQUAL((void*)0, p); 35 | } 36 | 37 | TEST(ValidCheckCloseSucceeds) 38 | { 39 | CHECK_CLOSE(2.0f, 2.001f, 0.01f); 40 | CHECK_CLOSE(2.001f, 2.0f, 0.01f); 41 | } 42 | 43 | TEST(ArrayCloseSucceeds) 44 | { 45 | float const a1[] = {1, 2, 3}; 46 | float const a2[] = {1, 2.01f, 3}; 47 | CHECK_ARRAY_CLOSE(a1, a2, 3, 0.1f); 48 | } 49 | 50 | TEST (CheckArrayCloseWorksWithVectors) 51 | { 52 | std::vector< float > a(4); 53 | for (int i = 0; i < 4; ++i) 54 | a[i] = (float)i; 55 | 56 | CHECK_ARRAY_CLOSE(a, a, (int)a.size(), 0.0001f); 57 | } 58 | 59 | TEST(CheckThrowMacroSucceedsOnCorrectException) 60 | { 61 | struct TestException {}; 62 | CHECK_THROW(throw TestException(), TestException); 63 | } 64 | 65 | TEST(CheckAssertSucceeds) 66 | { 67 | CHECK_ASSERT(UnitTest::ReportAssert("desc", "file", 0)); 68 | } 69 | 70 | TEST(CheckThrowMacroFailsOnMissingException) 71 | { 72 | class NoThrowTest : public UnitTest::Test 73 | { 74 | public: 75 | NoThrowTest() : Test("nothrow") {} 76 | void DontThrow() const 77 | { 78 | } 79 | 80 | virtual void RunImpl() const 81 | { 82 | CHECK_THROW(DontThrow(), int); 83 | } 84 | }; 85 | 86 | UnitTest::TestResults results; 87 | { 88 | ScopedCurrentTest scopedResults(results); 89 | 90 | NoThrowTest test; 91 | test.Run(); 92 | } 93 | 94 | CHECK_EQUAL(1, results.GetFailureCount()); 95 | } 96 | 97 | TEST(CheckThrowMacroFailsOnWrongException) 98 | { 99 | class WrongThrowTest : public UnitTest::Test 100 | { 101 | public: 102 | WrongThrowTest() : Test("wrongthrow") {} 103 | virtual void RunImpl() const 104 | { 105 | CHECK_THROW(throw "oops", int); 106 | } 107 | }; 108 | 109 | UnitTest::TestResults results; 110 | { 111 | ScopedCurrentTest scopedResults(results); 112 | 113 | WrongThrowTest test; 114 | test.Run(); 115 | } 116 | 117 | CHECK_EQUAL(1, results.GetFailureCount()); 118 | } 119 | 120 | struct SimpleFixture 121 | { 122 | SimpleFixture() 123 | { 124 | ++instanceCount; 125 | } 126 | ~SimpleFixture() 127 | { 128 | --instanceCount; 129 | } 130 | 131 | static int instanceCount; 132 | }; 133 | 134 | int SimpleFixture::instanceCount = 0; 135 | 136 | TEST_FIXTURE(SimpleFixture, DefaultFixtureCtorIsCalled) 137 | { 138 | CHECK(SimpleFixture::instanceCount > 0); 139 | } 140 | 141 | TEST_FIXTURE(SimpleFixture, OnlyOneFixtureAliveAtATime) 142 | { 143 | CHECK_EQUAL(1, SimpleFixture::instanceCount); 144 | } 145 | 146 | void CheckBool(const bool b) 147 | { 148 | CHECK(b); 149 | } 150 | 151 | TEST(CanCallCHECKOutsideOfTestFunction) 152 | { 153 | CheckBool(true); 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /UnitTest++/src/tests/TestXmlTestReporter.cpp: -------------------------------------------------------------------------------- 1 | #include "../UnitTest++.h" 2 | #include "../XmlTestReporter.h" 3 | 4 | #include 5 | 6 | using namespace UnitTest; 7 | using std::ostringstream; 8 | 9 | namespace 10 | { 11 | 12 | #ifdef UNITTEST_USE_CUSTOM_STREAMS 13 | 14 | // Overload to let MemoryOutStream accept std::string 15 | MemoryOutStream& operator<<(MemoryOutStream& s, const std::string& value) 16 | { 17 | s << value.c_str(); 18 | return s; 19 | } 20 | 21 | #endif 22 | 23 | struct XmlTestReporterFixture 24 | { 25 | XmlTestReporterFixture() 26 | : reporter(output) 27 | { 28 | } 29 | 30 | ostringstream output; 31 | XmlTestReporter reporter; 32 | }; 33 | 34 | TEST_FIXTURE(XmlTestReporterFixture, MultipleCharactersAreEscaped) 35 | { 36 | TestDetails const details("TestName", "suite", "filename.h", 4321); 37 | 38 | reporter.ReportTestStart(details); 39 | reporter.ReportFailure(details, "\"\"\'\'&&<<>>"); 40 | reporter.ReportTestFinish(details, 0.1f); 41 | reporter.ReportSummary(1, 2, 3, 0.1f); 42 | 43 | char const* expected = 44 | "" 45 | "" 46 | "" 47 | "" 49 | "" 50 | ""; 51 | 52 | CHECK_EQUAL(expected, output.str()); 53 | } 54 | 55 | TEST_FIXTURE(XmlTestReporterFixture, OutputIsCachedUntilReportSummaryIsCalled) 56 | { 57 | TestDetails const details("", "", "", 0); 58 | 59 | reporter.ReportTestStart(details); 60 | reporter.ReportFailure(details, "message"); 61 | reporter.ReportTestFinish(details, 1.0F); 62 | CHECK(output.str().empty()); 63 | 64 | reporter.ReportSummary(1, 1, 1, 1.0f); 65 | CHECK(!output.str().empty()); 66 | } 67 | 68 | TEST_FIXTURE(XmlTestReporterFixture, EmptyReportSummaryFormat) 69 | { 70 | reporter.ReportSummary(0, 0, 0, 0.1f); 71 | 72 | const char *expected = 73 | "" 74 | "" 75 | ""; 76 | 77 | CHECK_EQUAL(expected, output.str()); 78 | } 79 | 80 | TEST_FIXTURE(XmlTestReporterFixture, SingleSuccessfulTestReportSummaryFormat) 81 | { 82 | TestDetails const details("TestName", "DefaultSuite", "", 0); 83 | 84 | reporter.ReportTestStart(details); 85 | reporter.ReportSummary(1, 0, 0, 0.1f); 86 | 87 | const char *expected = 88 | "" 89 | "" 90 | "" 91 | ""; 92 | 93 | CHECK_EQUAL(expected, output.str()); 94 | } 95 | 96 | TEST_FIXTURE(XmlTestReporterFixture, SingleFailedTestReportSummaryFormat) 97 | { 98 | TestDetails const details("A Test", "suite", "A File", 4321); 99 | 100 | reporter.ReportTestStart(details); 101 | reporter.ReportFailure(details, "A Failure"); 102 | reporter.ReportSummary(1, 1, 1, 0.1f); 103 | 104 | const char *expected = 105 | "" 106 | "" 107 | "" 108 | "" 109 | "" 110 | ""; 111 | 112 | CHECK_EQUAL(expected, output.str()); 113 | } 114 | 115 | TEST_FIXTURE(XmlTestReporterFixture, FailureMessageIsXMLEscaped) 116 | { 117 | TestDetails const details("TestName", "suite", "filename.h", 4321); 118 | 119 | reporter.ReportTestStart(details); 120 | reporter.ReportFailure(details, "\"\'&<>"); 121 | reporter.ReportTestFinish(details, 0.1f); 122 | reporter.ReportSummary(1, 1, 1, 0.1f); 123 | 124 | char const* expected = 125 | "" 126 | "" 127 | "" 128 | "" 129 | "" 130 | ""; 131 | 132 | CHECK_EQUAL(expected, output.str()); 133 | } 134 | 135 | TEST_FIXTURE(XmlTestReporterFixture, OneFailureAndOneSuccess) 136 | { 137 | TestDetails const failedDetails("FailedTest", "suite", "fail.h", 1); 138 | reporter.ReportTestStart(failedDetails); 139 | reporter.ReportFailure(failedDetails, "expected 1 but was 2"); 140 | reporter.ReportTestFinish(failedDetails, 0.1f); 141 | 142 | TestDetails const succeededDetails("SucceededTest", "suite", "", 0); 143 | reporter.ReportTestStart(succeededDetails); 144 | reporter.ReportTestFinish(succeededDetails, 1.0f); 145 | reporter.ReportSummary(2, 1, 1, 1.1f); 146 | 147 | char const* expected = 148 | "" 149 | "" 150 | "" 151 | "" 152 | "" 153 | "" 154 | ""; 155 | 156 | CHECK_EQUAL(expected, output.str()); 157 | } 158 | 159 | TEST_FIXTURE(XmlTestReporterFixture, MultipleFailures) 160 | { 161 | TestDetails const failedDetails1("FailedTest", "suite", "fail.h", 1); 162 | TestDetails const failedDetails2("FailedTest", "suite", "fail.h", 31); 163 | 164 | reporter.ReportTestStart(failedDetails1); 165 | reporter.ReportFailure(failedDetails1, "expected 1 but was 2"); 166 | reporter.ReportFailure(failedDetails2, "expected one but was two"); 167 | reporter.ReportTestFinish(failedDetails1, 0.1f); 168 | 169 | reporter.ReportSummary(1, 1, 2, 1.1f); 170 | 171 | char const* expected = 172 | "" 173 | "" 174 | "" 175 | "" 176 | "" 177 | "" 178 | ""; 179 | 180 | CHECK_EQUAL(expected, output.str()); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /inih/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | The "inih" library is distributed under the New BSD license: 3 | 4 | Copyright (c) 2009, Brush Technology 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of Brush Technology nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /inih/README.txt: -------------------------------------------------------------------------------- 1 | 2 | inih is a simple .INI file parser written in C, released under the New BSD 3 | license (see LICENSE.txt). Go to the project home page for more info: 4 | 5 | http://code.google.com/p/inih/ 6 | -------------------------------------------------------------------------------- /inih/ini.c: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | http://code.google.com/p/inih/ 7 | 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "ini.h" 15 | 16 | #if !INI_USE_STACK 17 | #include 18 | #endif 19 | 20 | #define MAX_SECTION 50 21 | #define MAX_NAME 50 22 | 23 | /* Strip whitespace chars off end of given string, in place. Return s. */ 24 | static char* rstrip(char* s) 25 | { 26 | char* p = s + strlen(s); 27 | while (p > s && isspace((unsigned char)(*--p))) 28 | *p = '\0'; 29 | return s; 30 | } 31 | 32 | /* Return pointer to first non-whitespace char in given string. */ 33 | static char* lskip(const char* s) 34 | { 35 | while (*s && isspace((unsigned char)(*s))) 36 | s++; 37 | return (char*)s; 38 | } 39 | 40 | /* Return pointer to first char c or ';' comment in given string, or pointer to 41 | null at end of string if neither found. ';' must be prefixed by a whitespace 42 | character to register as a comment. */ 43 | static char* find_char_or_comment(const char* s, char c) 44 | { 45 | int was_whitespace = 0; 46 | while (*s && *s != c && !(was_whitespace && *s == ';')) { 47 | was_whitespace = isspace((unsigned char)(*s)); 48 | s++; 49 | } 50 | return (char*)s; 51 | } 52 | 53 | /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ 54 | static char* strncpy0(char* dest, const char* src, size_t size) 55 | { 56 | strncpy(dest, src, size); 57 | dest[size - 1] = '\0'; 58 | return dest; 59 | } 60 | 61 | /* See documentation in header file. */ 62 | int ini_parse_file(FILE* file, 63 | int (*handler)(void*, const char*, const char*, 64 | const char*), 65 | void* user) 66 | { 67 | /* Uses a fair bit of stack (use heap instead if you need to) */ 68 | #if INI_USE_STACK 69 | char line[INI_MAX_LINE]; 70 | #else 71 | char* line; 72 | #endif 73 | char section[MAX_SECTION] = ""; 74 | char prev_name[MAX_NAME] = ""; 75 | 76 | char* start; 77 | char* end; 78 | char* name; 79 | char* value; 80 | int lineno = 0; 81 | int error = 0; 82 | 83 | #if !INI_USE_STACK 84 | line = (char*)malloc(INI_MAX_LINE); 85 | if (!line) { 86 | return -2; 87 | } 88 | #endif 89 | 90 | /* Scan through file line by line */ 91 | while (fgets(line, INI_MAX_LINE, file) != NULL) { 92 | lineno++; 93 | 94 | start = line; 95 | #if INI_ALLOW_BOM 96 | if (lineno == 1 && (unsigned char)start[0] == 0xEF && 97 | (unsigned char)start[1] == 0xBB && 98 | (unsigned char)start[2] == 0xBF) { 99 | start += 3; 100 | } 101 | #endif 102 | start = lskip(rstrip(start)); 103 | 104 | if (*start == ';' || *start == '#') { 105 | /* Per Python ConfigParser, allow '#' comments at start of line */ 106 | } 107 | #if INI_ALLOW_MULTILINE 108 | else if (*prev_name && *start && start > line) { 109 | /* Non-black line with leading whitespace, treat as continuation 110 | of previous name's value (as per Python ConfigParser). */ 111 | if (!handler(user, section, prev_name, start) && !error) 112 | error = lineno; 113 | } 114 | #endif 115 | else if (*start == '[') { 116 | /* A "[section]" line */ 117 | end = find_char_or_comment(start + 1, ']'); 118 | if (*end == ']') { 119 | *end = '\0'; 120 | strncpy0(section, start + 1, sizeof(section)); 121 | *prev_name = '\0'; 122 | } 123 | else if (!error) { 124 | /* No ']' found on section line */ 125 | error = lineno; 126 | } 127 | } 128 | else if (*start && *start != ';') { 129 | /* Not a comment, must be a name[=:]value pair */ 130 | end = find_char_or_comment(start, '='); 131 | if (*end != '=') { 132 | end = find_char_or_comment(start, ':'); 133 | } 134 | if (*end == '=' || *end == ':') { 135 | *end = '\0'; 136 | name = rstrip(start); 137 | value = lskip(end + 1); 138 | end = find_char_or_comment(value, '\0'); 139 | if (*end == ';') 140 | *end = '\0'; 141 | rstrip(value); 142 | 143 | /* Valid name[=:]value pair found, call handler */ 144 | strncpy0(prev_name, name, sizeof(prev_name)); 145 | if (!handler(user, section, name, value) && !error) 146 | error = lineno; 147 | } 148 | else if (!error) { 149 | /* No '=' or ':' found on name[=:]value line */ 150 | error = lineno; 151 | } 152 | } 153 | } 154 | 155 | #if !INI_USE_STACK 156 | free(line); 157 | #endif 158 | 159 | return error; 160 | } 161 | 162 | /* See documentation in header file. */ 163 | int ini_parse(const char* filename, 164 | int (*handler)(void*, const char*, const char*, const char*), 165 | void* user) 166 | { 167 | FILE* file; 168 | int error; 169 | 170 | file = fopen(filename, "r"); 171 | if (!file) 172 | return -1; 173 | error = ini_parse_file(file, handler, user); 174 | fclose(file); 175 | return error; 176 | } 177 | -------------------------------------------------------------------------------- /inih/ini.h: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | http://code.google.com/p/inih/ 7 | 8 | */ 9 | 10 | #ifndef __INI_H__ 11 | #define __INI_H__ 12 | 13 | /* Make this header file easier to include in C++ code */ 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #include 19 | 20 | /* Parse given INI-style file. May have [section]s, name=value pairs 21 | (whitespace stripped), and comments starting with ';' (semicolon). Section 22 | is "" if name=value pair parsed before any section heading. name:value 23 | pairs are also supported as a concession to Python's ConfigParser. 24 | 25 | For each name=value pair parsed, call handler function with given user 26 | pointer as well as section, name, and value (data only valid for duration 27 | of handler call). Handler should return nonzero on success, zero on error. 28 | 29 | Returns 0 on success, line number of first error on parse error (doesn't 30 | stop on first error), -1 on file open error, or -2 on memory allocation 31 | error (only when INI_USE_STACK is zero). 32 | */ 33 | int ini_parse(const char* filename, 34 | int (*handler)(void* user, const char* section, 35 | const char* name, const char* value), 36 | void* user); 37 | 38 | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't 39 | close the file when it's finished -- the caller must do that. */ 40 | int ini_parse_file(FILE* file, 41 | int (*handler)(void* user, const char* section, 42 | const char* name, const char* value), 43 | void* user); 44 | 45 | /* Nonzero to allow multi-line value parsing, in the style of Python's 46 | ConfigParser. If allowed, ini_parse() will call the handler with the same 47 | name for each subsequent line parsed. */ 48 | #ifndef INI_ALLOW_MULTILINE 49 | #define INI_ALLOW_MULTILINE 1 50 | #endif 51 | 52 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of 53 | the file. See http://code.google.com/p/inih/issues/detail?id=21 */ 54 | #ifndef INI_ALLOW_BOM 55 | #define INI_ALLOW_BOM 1 56 | #endif 57 | 58 | /* Nonzero to use stack, zero to use heap (malloc/free). */ 59 | #ifndef INI_USE_STACK 60 | #define INI_USE_STACK 1 61 | #endif 62 | 63 | /* Maximum line length for any line in INI file. */ 64 | #ifndef INI_MAX_LINE 65 | #define INI_MAX_LINE 200 66 | #endif 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif /* __INI_H__ */ 73 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamnew123456/SmallWM/c2dc72afa87241bcf7e646630f4aae216ce78613/screenshot.png -------------------------------------------------------------------------------- /src/actions.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_ACTIONS__ 3 | #define __SMALLWM_ACTIONS__ 4 | 5 | #include "common.h" 6 | 7 | /// Indicates that an action will stick a window. 8 | const unsigned int ACT_STICK = 1 << 0, 9 | /// Indicates that an action will maximize a window. 10 | ACT_MAXIMIZE = 1 << 1, 11 | /// Indicates that an action will set the layer of a window. 12 | ACT_SETLAYER = 1 << 2, 13 | /// Indicates that an action will snap the window to the screen's edge. 14 | ACT_SNAP = 1 << 3, 15 | /// Moves a client in the X direction 16 | ACT_MOVE_X = 1 << 4, 17 | /// Moves a client in the Y direction 18 | ACT_MOVE_Y = 1 << 5, 19 | /// Packs the window into a corner, and manages location/size automatically 20 | ACT_PACK = 1 << 6; 21 | 22 | /** 23 | * A grouping of class actions which are applied to all clients of a particular class. 24 | * 25 | * ("Class" here refers to class in the X11 sense) 26 | */ 27 | struct ClassActions 28 | { 29 | /// Initialize a blank ClassAction, with a few defaults. 30 | ClassActions() : actions(0), snap(DIR_TOP), layer(0), 31 | relative_x(0), relative_y(0), 32 | pack_corner(PACK_NORTHWEST), pack_priority(0) 33 | {} 34 | 35 | /// All the actions which are applied; the flags are the values of ACT_*. 36 | unsigned int actions; 37 | 38 | /// The direction to snap a window. 39 | Direction snap; 40 | 41 | /// The layer to place the client on. 42 | Layer layer; 43 | 44 | /** The relative location (0% is the left/top, 100% is the bottom/right) of 45 | * the window. 46 | */ 47 | double relative_x, relative_y; 48 | 49 | /// What corner to pack the window into. 50 | PackCorner pack_corner; 51 | 52 | /// What priority to pack with (lower is closer to the edge) 53 | unsigned long pack_priority; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/clientmodel-events.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_CLIENTMODEL_EVENTS__ 3 | #define __SMALLWM_CLIENTMODEL_EVENTS__ 4 | 5 | #include 6 | #include 7 | 8 | #include "model/client-model.h" 9 | #include "model/focus-cycle.h" 10 | #include "model/x-model.h" 11 | #include "configparse.h" 12 | #include "common.h" 13 | #include "logging/logging.h" 14 | #include "utils.h" 15 | #include "xdata.h" 16 | 17 | /** 18 | * A dispatcher for handling the different change events raised by the 19 | * ClientModel. 20 | * 21 | * This serves as the linkage between changes in the client model, and changes 22 | * to the UI on the screen. 23 | */ 24 | class ClientModelEvents 25 | { 26 | public: 27 | ClientModelEvents(WMConfig &config, Log &logger, ChangeStream &changes, 28 | XData &xdata, ClientModel &clients, XModel &xmodel) : 29 | m_config(config), m_xdata(xdata), m_clients(clients), m_xmodel(xmodel), 30 | m_changes(changes), m_logger(logger), 31 | m_change(0), m_should_relayer(false), m_should_reposition_icons(false) 32 | {}; 33 | 34 | void handle_queued_changes(); 35 | 36 | private: 37 | void register_new_icon(Window, bool); 38 | Window create_placeholder(Window); 39 | void start_moving(Window); 40 | void start_resizing(Window); 41 | void do_relayer(); 42 | void reposition_icons(); 43 | void update_focus_cycle(); 44 | void update_location_size_for_cps(Window, ClientPosScale); 45 | 46 | void handle_layer_change(); 47 | void handle_focus_change(); 48 | void handle_client_desktop_change(); 49 | void handle_current_desktop_change(); 50 | void handle_screen_change(); 51 | void handle_mode_change(); 52 | void handle_location_change(); 53 | void handle_size_change(); 54 | void handle_destroy_change(); 55 | void handle_unmap_change(); 56 | 57 | void handle_new_client_desktop_change(Desktop* const, Window); 58 | void handle_client_change_from_user_desktop(Desktop* const, Desktop* const, Window); 59 | void handle_client_change_from_all_desktop(Desktop* const, Desktop* const, Window); 60 | void handle_client_change_from_icon_desktop(Desktop* const, Desktop* const, Window); 61 | void handle_client_change_from_moving_desktop(Desktop* const, Desktop* const, Window); 62 | void handle_client_change_from_resizing_desktop(Desktop* const, Desktop* const, Window); 63 | 64 | void map_all(const std::vector&); 65 | void unmap_unfocus_all(const std::vector&); 66 | void raise_family(Window); 67 | 68 | /// The stream of changes to read from 69 | ChangeStream &m_changes; 70 | 71 | /// The change that is currently being processed 72 | const Change *m_change; 73 | 74 | /// The configuration options that were given in the configuration file 75 | WMConfig &m_config; 76 | 77 | /// The data required to interface with Xlib 78 | XData &m_xdata; 79 | 80 | /// The data model which stores the clients and data about them 81 | ClientModel &m_clients; 82 | 83 | /** The data model which stores information related to clients, but not 84 | * about them. */ 85 | XModel &m_xmodel; 86 | 87 | /// The event handler's logger 88 | Log &m_logger; 89 | 90 | /** Whether or not to relayer the visible windows - this allows this class 91 | * to avoid restacking windows on every `ChangeLayer`, and instead only do 92 | * it once at the end of `handle_queued_changes`. */ 93 | bool m_should_relayer; 94 | 95 | /** Similar to `m_should_relayer`, this indicates whether the change handler 96 | * should reposition all the icon windows at the end of `handle_queued_changes`. 97 | */ 98 | bool m_should_reposition_icons; 99 | }; 100 | #endif 101 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_COMMON__ 3 | #define __SMALLWM_COMMON__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "logging/logging.h" 19 | 20 | /// A size of some kind. 21 | typedef int Dimension; 22 | /// The extend of some window or other surface. 23 | typedef std::pair Dimension2D; 24 | 25 | // Some convenience wrappers for accessing width-height pairs 26 | #define DIM2D_WIDTH(dim2d) ((dim2d).first) 27 | #define DIM2D_HEIGHT(dim2d) ((dim2d).second) 28 | // More convenience wrappers for accessing x-y pairs 29 | #define DIM2D_X(dim2d) ((dim2d).first) 30 | #define DIM2D_Y(dim2d) ((dim2d).second) 31 | 32 | // A box is exactly what is sounds like - a rectangular region 33 | struct Box { 34 | Box(): x(0), y(0), width(-1), height(-1) 35 | {} 36 | 37 | Box(int _x, int _y, Dimension _width, Dimension _height) : 38 | x(_x), y(_y), width(_width), height(_height) 39 | {} 40 | 41 | bool operator ==(const Box &other) const 42 | { 43 | return other.x == x && other.y == y && 44 | other.width == width && other.height == height; 45 | } 46 | 47 | bool operator !=(const Box &other) const 48 | { 49 | return !(other == *this); 50 | } 51 | 52 | int x, y; 53 | Dimension width, height; 54 | }; 55 | 56 | static std::ostream &operator<<(std::ostream &out, const Box &box) 57 | { 58 | out << "Box(" << box.x << "," << box.y << " " << 59 | box.width << "x" << box.height << ")"; 60 | return out; 61 | } 62 | 63 | /** 64 | * Directions are used both for snapping in the configuration loader, as well 65 | * as for moving windows to different relative screens. 66 | */ 67 | enum Direction 68 | { 69 | DIR_TOP = 1, 70 | DIR_BOTTOM, 71 | DIR_LEFT, 72 | DIR_RIGHT 73 | }; 74 | 75 | /** 76 | * The different corners that windows can be packed into. 77 | */ 78 | enum PackCorner 79 | { 80 | PACK_NORTHEAST, 81 | PACK_NORTHWEST, 82 | PACK_SOUTHEAST, 83 | PACK_SOUTHWEST 84 | }; 85 | 86 | // Note '(value) < (max)' - this is mostly used for finding screen boundaries, 87 | // and a window on the left edge of a screen should not be considered to be on 88 | // a different monitor 89 | #define IN_BOUNDS(value, min, max) ((value) >= (min) && (value) < (max)) 90 | 91 | /// The z-layer of a window. 92 | typedef unsigned char Layer; 93 | /// A difference between two layers. 94 | typedef char LayerDiff; 95 | 96 | /// The maximum layer of any non-dialog window. 97 | const Layer MAX_LAYER = 10, 98 | /// The default value for the layer type, representing an invalid 99 | /// layer 100 | INVALID_LAYER = 0, 101 | /// The lowest layer for any window. 102 | MIN_LAYER = 1, 103 | /// The layer of dialogs, which are on top of everything else. 104 | DIALOG_LAYER = 11, 105 | /// The default layer assigned to all windows. 106 | DEF_LAYER = 5; 107 | 108 | /// The button to click to launch a terminal 109 | const int LAUNCH_BUTTON = 1, 110 | /// The button to click to move a client 111 | MOVE_BUTTON = 1, 112 | /// The button to click to resize a client 113 | RESIZE_BUTTON = 3; 114 | 115 | // This is useful for tests, since they insist on printing things that they 116 | // have no knowledge of. 117 | template 118 | std::basic_ostream & 119 | operator<<(std::basic_ostream &out, const T &value) 120 | { 121 | out << "[Unknown Value]"; 122 | return out; 123 | } 124 | 125 | // Note that this would otherwise be a circular dependency between 126 | // model/changes.h and model/client-model.h 127 | 128 | /** 129 | * How a client is positioned and scaled - all but CPS_FLOATING are managed 130 | * by SmallWM. 131 | */ 132 | enum ClientPosScale 133 | { 134 | CPS_FLOATING, //< The user has manually moved/resized the window 135 | CPS_SPLIT_LEFT, //< A split on the left half of the screen 136 | CPS_SPLIT_RIGHT, 137 | CPS_SPLIT_TOP, 138 | CPS_SPLIT_BOTTOM, 139 | CPS_MAX, //< The window takes up the entire viewable area 140 | }; 141 | #endif 142 | -------------------------------------------------------------------------------- /src/configparse.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_CONFIG__ 3 | #define __SMALLWM_CONFIG__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ini.h" 13 | #include "actions.h" 14 | #include "common.h" 15 | #include "utils.h" 16 | 17 | /** 18 | * The different default keyboard shortcuts. 19 | */ 20 | enum KeyboardAction 21 | { 22 | INVALID_ACTION = 0, 23 | CLIENT_NEXT_DESKTOP, CLIENT_PREV_DESKTOP, 24 | NEXT_DESKTOP, PREV_DESKTOP, 25 | TOGGLE_STICK, 26 | ICONIFY, 27 | MAXIMIZE, 28 | REQUEST_CLOSE, FORCE_CLOSE, 29 | K_SNAP_TOP, K_SNAP_BOTTOM, K_SNAP_LEFT, K_SNAP_RIGHT, 30 | SCREEN_TOP, SCREEN_BOTTOM, SCREEN_LEFT, SCREEN_RIGHT, 31 | LAYER_ABOVE, LAYER_BELOW, LAYER_TOP, LAYER_BOTTOM, 32 | LAYER_1, LAYER_2, LAYER_3, LAYER_4, LAYER_5, LAYER_6, LAYER_7, LAYER_8, LAYER_9, 33 | CYCLE_FOCUS, CYCLE_FOCUS_BACK, 34 | EXIT_WM 35 | }; 36 | 37 | /** 38 | * Describes a relationship between: 39 | * - A keyboard shortcut 40 | * - The name in the configuration file 41 | * - The default keyboard shortcut 42 | * - Whether or not that shortcut uses the secondary action modifier 43 | */ 44 | struct DefaultShortcut 45 | { 46 | KeyboardAction action; 47 | const char *config_name; 48 | KeySym keysym; 49 | bool uses_secondary; 50 | }; 51 | 52 | /** 53 | * Key bindings consist of both a main key, a modifier, and (possibly) 54 | * a secondary modifier (the boolean). 55 | */ 56 | typedef std::pair KeyBinding; 57 | 58 | /** 59 | * All the keyboard controls, which can loaded from the config file. 60 | */ 61 | struct KeyboardConfig 62 | { 63 | // Initialize the default keyboard bindings. 64 | KeyboardConfig() 65 | { 66 | reset(); 67 | }; 68 | 69 | void reset() 70 | { 71 | action_to_binding.clear(); 72 | binding_to_action.clear(); 73 | 74 | DefaultShortcut shortcuts[] = { 75 | { CLIENT_NEXT_DESKTOP, "client-next-desktop", XK_bracketright, false }, 76 | { CLIENT_PREV_DESKTOP, "client-prev-desktop", XK_bracketleft, false }, 77 | { NEXT_DESKTOP, "next-desktop", XK_period, false }, 78 | { PREV_DESKTOP, "prev-desktop", XK_comma, false }, 79 | { TOGGLE_STICK, "toggle-stick", XK_backslash, false }, 80 | { ICONIFY, "iconify", XK_h, false }, 81 | { MAXIMIZE, "maximize", XK_m, false }, 82 | { REQUEST_CLOSE, "request-close", XK_c, false }, 83 | { FORCE_CLOSE, "force-close", XK_x, false }, 84 | { K_SNAP_TOP, "snap-top", XK_Up, false }, 85 | { K_SNAP_BOTTOM, "snap-bottom", XK_Down, false }, 86 | { K_SNAP_LEFT, "snap-left", XK_Left, false }, 87 | { K_SNAP_RIGHT, "snap-right", XK_Right, false }, 88 | { SCREEN_TOP, "screen-top", XK_Up, true }, 89 | { SCREEN_BOTTOM, "screen-bottom", XK_Down, true }, 90 | { SCREEN_LEFT, "screen-left", XK_Left, true }, 91 | { SCREEN_RIGHT, "screen-right", XK_Right, true }, 92 | { LAYER_ABOVE, "layer-above", XK_Page_Up, false }, 93 | { LAYER_BELOW, "layer-below", XK_Page_Down, false }, 94 | { LAYER_TOP, "layer-top", XK_Home, false }, 95 | { LAYER_BOTTOM, "layer-bottom", XK_End, false }, 96 | { LAYER_1, "layer-1", XK_1, false }, 97 | { LAYER_2, "layer-2", XK_2, false }, 98 | { LAYER_3, "layer-3", XK_3, false }, 99 | { LAYER_4, "layer-4", XK_4, false }, 100 | { LAYER_5, "layer-5", XK_5, false }, 101 | { LAYER_6, "layer-6", XK_6, false }, 102 | { LAYER_7, "layer-7", XK_7, false }, 103 | { LAYER_8, "layer-8", XK_8, false }, 104 | { LAYER_9, "layer-9", XK_9, false }, 105 | { CYCLE_FOCUS, "cycle-focus", XK_Tab, false }, 106 | { CYCLE_FOCUS_BACK, "cycle-focus-back", XK_Tab, true }, 107 | { EXIT_WM, "exit", XK_Escape, false }, 108 | }; 109 | 110 | int num_shortcuts = sizeof(shortcuts) / sizeof(shortcuts[0]); 111 | for (int i = 0; i < num_shortcuts; i++) 112 | { 113 | DefaultShortcut ¤t_shortcut = shortcuts[i]; 114 | if (current_shortcut.config_name != NULL) 115 | { 116 | std::string config_name = std::string(current_shortcut.config_name); 117 | action_names[config_name] = current_shortcut.action; 118 | } 119 | 120 | KeyBinding binding(current_shortcut.keysym, current_shortcut.uses_secondary); 121 | 122 | action_to_binding[current_shortcut.action] = binding; 123 | binding_to_action[binding] = current_shortcut.action; 124 | } 125 | } 126 | 127 | /// The keyboard shortcuts which are bound to specific keys 128 | std::map action_to_binding; 129 | 130 | /// The configuration values which are bound to specific shortcuts 131 | std::map action_names; 132 | 133 | /// A reverse mapping between KeyBindings and KeyboardActions 134 | std::map binding_to_action; 135 | }; 136 | 137 | /** 138 | * How to apply actions made with keyboard hotkeys. 139 | */ 140 | enum HotkeyType 141 | { 142 | HK_FOCUS, //< Hotkeys apply to the currently focused window 143 | HK_MOUSE //< Hotkeys apply to the window the mouse cursor is on 144 | }; 145 | 146 | /** 147 | * Reads and manages configuration options in the SmallWM configure option. 148 | */ 149 | class WMConfig 150 | { 151 | public: 152 | /// Loads a couple of defaults for the WMConfig. 153 | WMConfig() 154 | { reset(); }; 155 | 156 | void load(); 157 | void reset(); 158 | 159 | /// The current hotkey mode 160 | HotkeyType hotkey; 161 | 162 | /// The minimum message level to send to syslog 163 | int log_mask; 164 | 165 | /// The filename to dump logs in, or "syslog" to use syslog 166 | std::string log_file; 167 | 168 | /// The shell to run. 169 | std::string shell; 170 | 171 | /// The window manager's keyboard bindings. 172 | KeyboardConfig key_commands; 173 | 174 | /// The number of available desktops. 175 | unsigned long long num_desktops; 176 | 177 | /// The width of hidden icons. 178 | Dimension icon_width, 179 | /// The height of the hidden icons. 180 | icon_height; 181 | 182 | /// The width of the window border. 183 | Dimension border_width; 184 | 185 | /// Handles all the configured class actions. 186 | std::map classactions; 187 | 188 | /// All of the X11 classes which should not be focusable by default 189 | std::vector no_autofocus; 190 | 191 | /// Whether or not to show images inside icons for hidden windows 192 | bool show_icons; 193 | 194 | /// The filename to dump the current state to when SIGUSR1 is received 195 | std::string dump_file; 196 | 197 | protected: 198 | virtual std::string get_config_path() const; 199 | 200 | private: 201 | static int config_parser(void *user, const char *c_section, const char *c_name, const char *c_value); 202 | }; 203 | 204 | #endif 205 | -------------------------------------------------------------------------------- /src/logging/file.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "logging/file.h" 3 | 4 | /** 5 | * Stops accepting log messages. 6 | */ 7 | void FileLog::stop() 8 | { 9 | m_stopped = true; 10 | } 11 | 12 | /** 13 | * Sets the current log message priority, and starts building up a log message. 14 | */ 15 | Log &FileLog::log(int priority) 16 | { 17 | m_priority = priority; 18 | return *this; 19 | } 20 | 21 | /** 22 | * Adds a new fragment to the current log message. 23 | */ 24 | void FileLog::write(std::string &str) 25 | { 26 | m_formatter << str; 27 | } 28 | 29 | /** 30 | * Completes the current log message and writes it to the log file. 31 | */ 32 | void FileLog::flush() 33 | { 34 | if (!m_stopped && m_priority < m_level) 35 | { 36 | std::fstream output(m_filename.c_str(), 37 | std::fstream::out | std::fstream::app); 38 | 39 | output << std::time(NULL) 40 | << " " 41 | << m_priority 42 | << ": " 43 | << m_formatter.str() 44 | << "\n"; 45 | 46 | output.close(); 47 | } 48 | 49 | m_formatter.str(""); 50 | } 51 | -------------------------------------------------------------------------------- /src/logging/file.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_LOGGING_FILE__ 3 | #define __SMALLWM_LOGGING_FILE__ 4 | 5 | #include "logging/logging.h" 6 | 7 | #include 8 | #include 9 | 10 | class FileLog : public Log 11 | { 12 | public: 13 | FileLog(std::string filename, int level) : 14 | m_level(level), 15 | m_filename(filename) 16 | {} 17 | 18 | void stop(); 19 | 20 | Log &log(int); 21 | void write(std::string &message); 22 | void flush(); 23 | 24 | private: 25 | // Whether or not the user actually started to log something 26 | bool m_stopped; 27 | 28 | // The name of the file to log messages to 29 | std::string m_filename; 30 | 31 | // What level to log messages at 32 | int m_level; 33 | 34 | // The log level of the current message 35 | int m_priority; 36 | 37 | // A string stream kept around to punt the operator<< formatting duties to 38 | std::ostringstream m_formatter; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/logging/logging.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "logging/logging.h" 3 | 4 | /** 5 | * Does special processing for Log::endl. 6 | * @param manipulator This should only ever by Log::endl. 7 | */ 8 | Log &Log::operator<<(LogManipulator manipulator) 9 | { 10 | return manipulator(*this); 11 | } 12 | 13 | /** 14 | * Flushes the current messages to syslog, and then becomes ready for a new 15 | * message. 16 | * @param stream The stream to operate upon. 17 | */ 18 | Log &Log::endl(Log &stream) 19 | { 20 | stream.flush(); 21 | return stream; 22 | }; 23 | -------------------------------------------------------------------------------- /src/logging/logging.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_LOGGING__ 3 | #define __SMALLWM_LOGGING__ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | /** 13 | * A basic logging API, which can be used to define the various kinds of 14 | * loggers. 15 | */ 16 | class Log 17 | { 18 | public: 19 | virtual void stop() = 0; 20 | 21 | virtual Log &log(int) = 0; 22 | virtual void write(std::string&) = 0; 23 | virtual void flush() = 0; 24 | 25 | template 26 | Log &operator<<(T value) 27 | { 28 | m_formatter.str(""); 29 | m_formatter << value; 30 | 31 | std::string format_out = m_formatter.str(); 32 | this->write(format_out); 33 | return *this; 34 | } 35 | 36 | Log &operator<<(Log& (*manipulator)(Log&)); 37 | 38 | static Log &endl(Log &stream); 39 | 40 | protected: 41 | // A utility object, used to input things via << 42 | std::ostringstream m_formatter; 43 | }; 44 | 45 | /// The type of Log::endl 46 | typedef Log& (*LogManipulator)(Log&); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/logging/stream.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "logging/stream.h" 3 | 4 | /** 5 | * Closes a StreamLog. Note that this doesn't close the underlying output 6 | * stream - it just prevents anything else from being logged. 7 | */ 8 | void StreamLog::stop() 9 | { 10 | m_closed = true; 11 | } 12 | 13 | /** 14 | * Prepares to write a log message (normally, this sets up the priority of the 15 | * next log, but this doesn't record that information). 16 | */ 17 | Log &StreamLog::log(int priority) 18 | { 19 | return *this; 20 | } 21 | 22 | /** 23 | * Writes the given string to the output stream. 24 | */ 25 | void StreamLog::write(std::string &str) 26 | { 27 | if (!m_closed) 28 | m_stream << str; 29 | } 30 | 31 | /** 32 | * Writes a newline to the output stream. 33 | */ 34 | void StreamLog::flush() 35 | { 36 | if (!m_closed) 37 | m_stream << std::endl; 38 | } 39 | -------------------------------------------------------------------------------- /src/logging/stream.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_LOGGING_SYSLOG__ 3 | #define __SMALLWM_LOGGING_SYSLOG__ 4 | 5 | #include "logging/logging.h" 6 | 7 | /** 8 | * A Log which writes to a C++ ostream. 9 | */ 10 | class StreamLog : public Log 11 | { 12 | public: 13 | StreamLog(std::ostream &stream) : 14 | m_stream(stream), m_closed(false) 15 | {} 16 | 17 | void stop(); 18 | 19 | Log &log(int); 20 | void write(std::string&); 21 | void flush(); 22 | 23 | private: 24 | // The stream to write log messages to 25 | std::ostream &m_stream; 26 | 27 | // Whether or not the log is closed for future writes 28 | bool m_closed; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/logging/syslog.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "logging/syslog.h" 3 | 4 | /** 5 | * Adds an option to the options flag given to syslog. 6 | * @param syslog_option An option, provided by 7 | */ 8 | void SysLog::add_option(int syslog_option) 9 | { 10 | m_options |= syslog_option; 11 | }; 12 | 13 | /** 14 | * Removes an option from the options flag given to syslog. 15 | * @param syslog_option An option, provided by 16 | */ 17 | void SysLog::remove_option(int syslog_option) 18 | { 19 | m_options &= ~syslog_option; 20 | }; 21 | 22 | /** 23 | * Sets the name to appear before all of the program's logs. 24 | * @param syslog_ident The program's name. 25 | */ 26 | void SysLog::set_identity(std::string syslog_ident) 27 | { 28 | m_identity = syslog_ident; 29 | }; 30 | 31 | /** 32 | * Sets the facility to use when logging messages. 33 | * @param syslog_facility The output facility to use. 34 | */ 35 | void SysLog::set_facility(int syslog_facility) 36 | { 37 | m_facility = syslog_facility; 38 | }; 39 | 40 | /** 41 | * Sets which logging messages are handled by syslog. 42 | * @note Use the LOG_MASK and LOG_UPTO macros to construct this flag. 43 | * @param mask The logging mask to let syslog handle. 44 | */ 45 | void SysLog::set_log_mask(int syslog_logmask) 46 | { 47 | setlogmask(syslog_logmask); 48 | } 49 | 50 | /** 51 | * Begins accepting log messages. 52 | * @note After this, calling: 53 | * - set_identity 54 | * - add_options / remove_option 55 | * - set_facility 56 | * Will do nothing. 57 | */ 58 | void SysLog::start() 59 | { 60 | if (!m_started) 61 | { 62 | openlog(m_identity.c_str(), m_options, m_facility); 63 | m_started = true; 64 | } 65 | }; 66 | 67 | /** 68 | * Stops accepting log messages. 69 | */ 70 | void SysLog::stop() 71 | { 72 | if (m_started) 73 | { 74 | closelog(); 75 | 76 | // Although this logger is intended to be started once, when the 77 | // program launches, be consistent and do this anyway. 78 | m_started = false; 79 | } 80 | }; 81 | 82 | 83 | /** 84 | * This is the main entry point to SysLog, which sets the priority and returns 85 | * itself for use with the << operator and Log::endl. 86 | * @param syslog_priority The minimum priority level. 87 | */ 88 | Log &SysLog::log(int syslog_priority) 89 | { 90 | m_priority = syslog_priority; 91 | return *this; 92 | }; 93 | 94 | 95 | /** 96 | * Buffers writes from the Log, so we can flush them later. 97 | */ 98 | void SysLog::write(std::string &str) 99 | { 100 | m_formatter << str; 101 | } 102 | 103 | /** 104 | * Flushes the content of a SysLog by writing it to syslog(). 105 | */ 106 | void SysLog::flush() 107 | { 108 | if (m_started) 109 | syslog(m_priority, "%s", m_formatter.str().c_str()); 110 | 111 | m_formatter.str(""); 112 | } 113 | 114 | -------------------------------------------------------------------------------- /src/logging/syslog.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_LOGGING_SYSLOG__ 3 | #define __SMALLWM_LOGGING_SYSLOG__ 4 | 5 | #include "logging/logging.h" 6 | 7 | #include 8 | 9 | /** 10 | * A C++ friendly wrapper around the standard Unix syslog API, but adapted to the 11 | * ostreams syntax. 12 | */ 13 | class SysLog : public Log 14 | { 15 | public: 16 | SysLog() : 17 | m_identity("my-program"), 18 | m_options(0), 19 | m_facility(0), 20 | m_priority(LOG_INFO) 21 | {}; 22 | 23 | /** 24 | * Terminates the connection to syslog when the object is destroyed. 25 | */ 26 | ~SysLog() 27 | { 28 | stop(); 29 | }; 30 | 31 | void add_option(int); 32 | void remove_option(int); 33 | 34 | void set_identity(std::string); 35 | void set_facility(int); 36 | void set_log_mask(int); 37 | 38 | void start(); 39 | void stop(); 40 | 41 | Log &log(int); 42 | void write(std::string&); 43 | void flush(); 44 | 45 | private: 46 | // Whether or not the user actually started to log something 47 | bool m_started; 48 | // What identity to use for this logger 49 | std::string m_identity; 50 | // What options to send to syslog 51 | int m_options; 52 | // What facility to use for syslog 53 | int m_facility; 54 | // How sever the message is 55 | int m_priority; 56 | // A string stream kept around to punt the operator<< formatting duties to 57 | std::ostringstream m_formatter; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/model/changes.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "changes.h" 3 | 4 | /** 5 | * Returns true if there are changes to be processed, or false otherwise. 6 | */ 7 | bool ChangeStream::has_more() 8 | { 9 | return !m_changes.empty(); 10 | } 11 | 12 | /** 13 | * Gets the next change, or NULL if no changes remain. 14 | * 15 | * Note that this change should be deleted after the caller is finished with 16 | * it. 17 | */ 18 | ChangeStream::change_ptr ChangeStream::get_next() 19 | { 20 | if (has_more()) 21 | { 22 | change_ptr change = m_changes.front(); 23 | m_changes.pop(); 24 | return change; 25 | } 26 | else 27 | return 0; 28 | } 29 | 30 | /** 31 | * Pushes a change into the change buffer. 32 | */ 33 | void ChangeStream::push(change_ptr change) 34 | { 35 | m_changes.push(change); 36 | } 37 | 38 | /** 39 | * Removes all changes which are still stored. 40 | */ 41 | void ChangeStream::flush() 42 | { 43 | change_ptr change; 44 | while ((change = get_next()) != 0) 45 | delete change; 46 | } 47 | -------------------------------------------------------------------------------- /src/model/client-model.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_CLIENT_MODEL__ 3 | #define __SMALLWM_CLIENT_MODEL__ 4 | 5 | #include "changes.h" 6 | #include "common.h" 7 | #include "desktop-type.h" 8 | #include "screen.h" 9 | #include "unique-multimap.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | enum InitialState 19 | { 20 | IS_VISIBLE, 21 | IS_HIDDEN, 22 | }; 23 | 24 | /** 25 | * This defines the data model used for the client. 26 | * 27 | * This is intended to be totally divorced from Xlib, and is meant to do 28 | * transformations of data relating to clients and validations of that data. 29 | */ 30 | class ClientModel 31 | { 32 | private: 33 | typedef Change const * change_ptr; 34 | 35 | public: 36 | Desktop* ALL_DESKTOPS; 37 | Desktop* ICON_DESKTOP; 38 | Desktop* MOVING_DESKTOP; 39 | Desktop* RESIZING_DESKTOP; 40 | std::vector USER_DESKTOPS; 41 | 42 | typedef UniqueMultimap::member_iter client_iter; 43 | 44 | /** 45 | * Initializes all of the categories in the maps 46 | */ 47 | ClientModel(ChangeStream &changes, 48 | CrtManager &crt_manager, 49 | unsigned long long max_desktops, 50 | Dimension border_width) : 51 | m_crt_manager(crt_manager), 52 | m_changes(changes), 53 | m_max_desktops(max_desktops), 54 | m_border_width(border_width), 55 | m_focused(None), 56 | // Initialize all the desktops 57 | ALL_DESKTOPS(new AllDesktops()), 58 | ICON_DESKTOP(new IconDesktop()), 59 | MOVING_DESKTOP(new MovingDesktop()), 60 | RESIZING_DESKTOP(new ResizingDesktop()) 61 | { 62 | m_desktops.add_category(ALL_DESKTOPS); 63 | m_desktops.add_category(ICON_DESKTOP); 64 | m_desktops.add_category(MOVING_DESKTOP); 65 | m_desktops.add_category(RESIZING_DESKTOP); 66 | 67 | FocusCycle &all_cycle = dynamic_cast(ALL_DESKTOPS)->focus_cycle; 68 | for (unsigned long long desktop = 0; desktop < max_desktops; 69 | desktop++) 70 | { 71 | USER_DESKTOPS.push_back(new UserDesktop(desktop)); 72 | USER_DESKTOPS[desktop]->focus_cycle.set_subcycle(all_cycle); 73 | m_desktops.add_category(USER_DESKTOPS[desktop]); 74 | } 75 | 76 | for (Layer layer = MIN_LAYER; layer <= MAX_LAYER; layer++) 77 | m_layers.add_category(layer); 78 | 79 | m_current_desktop = USER_DESKTOPS[0]; 80 | } 81 | 82 | ~ClientModel() 83 | { 84 | m_changes.flush(); 85 | } 86 | 87 | bool is_client(Window); 88 | bool is_visible(Window); 89 | bool is_visible_desktop(Desktop*); 90 | bool is_child(Window); 91 | 92 | void get_clients_of(Desktop*, std::vector&); 93 | void get_visible_clients(std::vector&); 94 | void get_visible_in_layer_order(std::vector&); 95 | Window get_parent_of(Window); 96 | void get_children_of(Window, std::vector&); 97 | 98 | void add_client(Window, InitialState, Dimension2D, Dimension2D, bool); 99 | void remove_client(Window); 100 | void remap_client(Window); 101 | void unmap_client(Window); 102 | 103 | void add_child(Window, Window); 104 | void remove_child(Window, bool); 105 | 106 | void pack_client(Window, PackCorner, unsigned long); 107 | bool is_packed_client(Window); 108 | PackCorner get_pack_corner(Window); 109 | void repack_corner(PackCorner); 110 | 111 | void cycle_focus_forward(); 112 | void cycle_focus_backward(); 113 | 114 | ClientPosScale get_mode(Window); 115 | void change_mode(Window, ClientPosScale); 116 | 117 | void change_location(Window, Dimension, Dimension); 118 | void change_size(Window, Dimension, Dimension); 119 | void update_size(Window, Dimension, Dimension); 120 | 121 | Window get_focused(); 122 | bool is_autofocusable(Window); 123 | void set_autofocus(Window, bool); 124 | 125 | void focus(Window); 126 | void force_focus(Window); 127 | void unfocus(); 128 | void unfocus_if_focused(Window); 129 | 130 | Desktop* find_desktop(Window); 131 | Layer find_layer(Window); 132 | 133 | void up_layer(Window); 134 | void down_layer(Window); 135 | void set_layer(Window, Layer); 136 | 137 | void toggle_stick(Window); 138 | void client_reset_desktop(Window); 139 | void client_next_desktop(Window); 140 | void client_prev_desktop(Window); 141 | void next_desktop(); 142 | void prev_desktop(); 143 | 144 | void iconify(Window); 145 | void deiconify(Window); 146 | 147 | void start_moving(Window); 148 | void stop_moving(Window, Dimension2D); 149 | void start_resizing(Window); 150 | void stop_resizing(Window, Dimension2D); 151 | 152 | const Box &get_root_screen() const; 153 | const Box &get_screen(Window) const; 154 | void to_relative_screen(Window, Direction); 155 | void to_screen_box(Window, Box); 156 | 157 | void update_screens(std::vector&); 158 | 159 | void dump(std::ostream&); 160 | 161 | protected: 162 | void unfocus(bool); 163 | 164 | void move_to_desktop(Window, Desktop*, bool); 165 | 166 | void to_screen_crt(Window, Crt*); 167 | 168 | void sync_focus_to_cycle(); 169 | 170 | void dump_client_info(Window, std::ostream&); 171 | 172 | private: 173 | // The screen manager, used to map positions to screens 174 | CrtManager &m_crt_manager; 175 | 176 | /// The current change stream, which accepts changes we push 177 | ChangeStream &m_changes; 178 | 179 | /// The maximum number of user-visible desktops 180 | unsigned long long m_max_desktops; 181 | 182 | /// The size of window borders 183 | Dimension m_border_width; 184 | 185 | /// A mapping between clients and their desktops 186 | UniqueMultimap> m_desktops; 188 | /// A mapping between clients and the layers they inhabit 189 | UniqueMultimap m_layers; 190 | /// A mapping between clients and their locations 191 | std::map m_location; 192 | /// A mapping between clients and their sizes 193 | std::map m_size; 194 | /// A mapping between clients and their position/scale modes 195 | std::map m_cps_mode; 196 | /// A mapping between clients and their screens 197 | std::map m_screen; 198 | 199 | /// Which clients may be auto-focused, and which may not 200 | std::map m_autofocus; 201 | 202 | /** A mapping between clients that are iconified, or being moved/resized, 203 | and whether or not they were stuck before they were moved/resized or 204 | iconfied. */ 205 | std::map m_was_stuck; 206 | 207 | /** 208 | * A mapping between clients and their packing corner. 209 | */ 210 | std::map m_pack_corners; 211 | 212 | /** 213 | * A mapping between clients and their packing priorities. 214 | */ 215 | std::map m_pack_priority; 216 | 217 | /** 218 | * This maps between clients and their child windows. 219 | */ 220 | std::map*> m_children; 221 | 222 | /** 223 | * A mapping between child windows and their parents. 224 | */ 225 | std::map m_parents; 226 | 227 | /// The currently visible desktop 228 | UserDesktop * m_current_desktop; 229 | 230 | /// The currently focused client 231 | Window m_focused; 232 | }; 233 | 234 | #endif 235 | -------------------------------------------------------------------------------- /src/model/desktop-type.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_DESKTOP_TYPE__ 3 | #define __SMALLWM_DESKTOP_TYPE__ 4 | 5 | #include 6 | 7 | #include "model/focus-cycle.h" 8 | #include "utils.h" 9 | 10 | /* 11 | * These constants are used to make sure that different instances of 12 | * desktop classes with the same parameters are equal, and are stored in 13 | * the same place in an std::map 14 | */ 15 | const unsigned long long DESKTOP_SORT_KEY = 1, 16 | ALL_DESKTOP_SORT_KEY = 2, 17 | ICON_DESKTOP_SORT_KEY = 3, 18 | MOVING_DESKTOP_SORT_KEY = 4, 19 | RESIZING_DESKTOP_SORT_KEY = 5, 20 | USER_DESKTOP_SORT_KEY = 6; 21 | 22 | /** 23 | * This describes both 'real' desktops that the user interacts with, and 24 | * 'virtual' desktops which are used as a part of the data model. 25 | * 26 | * In essence, the term 'desktop' is used to describe any group of windows 27 | * to which a window must belong exclusively - it cannot be on two desktops 28 | * at once. This causes some issues (hence `AllDesktops`) but it works well 29 | * in practice. 30 | */ 31 | struct Desktop 32 | { 33 | Desktop(const unsigned long long _sort_key) : sort_key(_sort_key) 34 | {}; 35 | 36 | Desktop() : sort_key(DESKTOP_SORT_KEY) 37 | {}; 38 | 39 | virtual ~Desktop() 40 | {}; 41 | 42 | virtual bool is_user_desktop() const 43 | { return false; } 44 | 45 | virtual bool is_all_desktop() const 46 | { return false; } 47 | 48 | virtual bool is_icon_desktop() const 49 | { return false; } 50 | 51 | virtual bool is_moving_desktop() const 52 | { return false; } 53 | 54 | virtual bool is_resizing_desktop() const 55 | { return false; } 56 | 57 | bool operator<(const Desktop &other) const 58 | { return sort_key < other.sort_key; } 59 | 60 | bool operator==(const Desktop &other) const 61 | { return sort_key == other.sort_key; } 62 | 63 | const unsigned long long sort_key; 64 | }; 65 | 66 | /** 67 | * A 'real' desktop which the user directly interacts with. 68 | * 69 | * Note that this has a special artifact that other desktops do not - it keeps 70 | * track of its focus history, so that when the user returns to it, their 71 | * focus is preserved to the last window they were on. 72 | */ 73 | struct UserDesktop : public Desktop 74 | { 75 | UserDesktop(const unsigned long long _desktop) : 76 | desktop(_desktop), Desktop(_desktop + USER_DESKTOP_SORT_KEY) 77 | {}; 78 | 79 | bool is_user_desktop() const 80 | { return true; } 81 | 82 | const unsigned long long desktop; 83 | FocusCycle focus_cycle; 84 | }; 85 | 86 | static std::ostream &operator<<(std::ostream &out, const UserDesktop &desktop) 87 | { 88 | out << "[UserDesktop " << desktop.desktop << "]"; 89 | return out; 90 | } 91 | 92 | /** 93 | * A virtual desktop describing windows which are visible on all 'real' 94 | * desktops. 95 | */ 96 | struct AllDesktops : public Desktop 97 | { 98 | AllDesktops() : Desktop(ALL_DESKTOP_SORT_KEY) 99 | {}; 100 | 101 | bool is_all_desktop() const 102 | { return true; } 103 | 104 | FocusCycle focus_cycle; 105 | }; 106 | 107 | static std::ostream &operator<<(std::ostream &out, const AllDesktops &desktop) 108 | { 109 | out << "[All Desktops]"; 110 | return out; 111 | } 112 | 113 | /** 114 | * A virtual desktop describing windows which are currently hidden. 115 | */ 116 | struct IconDesktop : public Desktop 117 | { 118 | IconDesktop() : Desktop(ICON_DESKTOP_SORT_KEY) 119 | {}; 120 | 121 | bool is_icon_desktop() const 122 | { return true; } 123 | }; 124 | 125 | static std::ostream &operator<<(std::ostream &out, const IconDesktop &desktop) 126 | { 127 | out << "[Icon Desktop]"; 128 | return out; 129 | } 130 | 131 | /** 132 | * A virtual desktop for a window which is currently being moved. 133 | */ 134 | struct MovingDesktop : public Desktop 135 | { 136 | MovingDesktop() : Desktop(MOVING_DESKTOP_SORT_KEY) 137 | {}; 138 | 139 | bool is_moving_desktop() const 140 | { return true; } 141 | }; 142 | 143 | static std::ostream &operator<<(std::ostream &out, const MovingDesktop &desktop) 144 | { 145 | out << "[Moving Desktop]"; 146 | return out; 147 | } 148 | 149 | /** 150 | * A virtual desktop for a window which is currently being resized. 151 | */ 152 | struct ResizingDesktop : public Desktop 153 | { 154 | ResizingDesktop() : Desktop(RESIZING_DESKTOP_SORT_KEY) 155 | {}; 156 | 157 | bool is_resizing_desktop() const 158 | { return true; } 159 | }; 160 | 161 | static std::ostream &operator<<(std::ostream &out, const ResizingDesktop &desktop) 162 | { 163 | out << "[Resizing Desktop]"; 164 | return out; 165 | } 166 | 167 | static std::ostream &operator<<(std::ostream &out, const Desktop &desktop) 168 | { 169 | if (desktop.is_user_desktop()) 170 | out << dynamic_cast(desktop); 171 | else if (desktop.is_all_desktop()) 172 | out << dynamic_cast(desktop); 173 | else if (desktop.is_icon_desktop()) 174 | out << dynamic_cast(desktop); 175 | else if (desktop.is_moving_desktop()) 176 | out << dynamic_cast(desktop); 177 | else if (desktop.is_resizing_desktop()) 178 | out << dynamic_cast(desktop); 179 | else 180 | out << "[Desktop]"; 181 | 182 | return out; 183 | } 184 | 185 | /** 186 | * Like std::less, but for pointers. 187 | */ 188 | template 189 | struct PointerLess 190 | { 191 | /** 192 | * Compares a pair of Desktop pointers. This is used because Desktop must be 193 | * stored in structures as a pointer, due to the need for downcasting to 194 | * access appropriate members. 195 | * 196 | * Note that this also handles NULL pointers, by saying that a NULL pointer 197 | * is less than any other pointer, with the exception of other null 198 | * pointers. 199 | */ 200 | bool operator()(const T *a, const T* b) const 201 | { 202 | if (!a && !b) 203 | return false; 204 | else if (!a) 205 | return true; 206 | else if (!b) 207 | return false; 208 | else 209 | return *a < *b; 210 | } 211 | }; 212 | 213 | #endif 214 | -------------------------------------------------------------------------------- /src/model/focus-cycle.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_FOCUS_CYCLE__ 3 | #define __SMALLWM_FOCUS_CYCLE__ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common.h" 10 | 11 | /** 12 | * Handles a focus cycle for a group of windows, which provides an order 13 | * across those windows as well as a 'cursor' that can be used to move 14 | * the focus forward/backward. 15 | */ 16 | class FocusCycle 17 | { 18 | public: 19 | FocusCycle() : 20 | m_currently_focused(false), 21 | m_has_subcycle(false), 22 | m_subcycle_in_use(false), 23 | m_subcycle(NULL) 24 | {}; 25 | 26 | void add(Window); 27 | void add_after(Window, Window); 28 | bool remove(Window, bool); 29 | 30 | void set_subcycle(FocusCycle&); 31 | void clear_subcycle(); 32 | 33 | bool empty() const; 34 | bool valid() const; 35 | Window get() const; 36 | bool set(Window); 37 | void unset(); 38 | 39 | bool forward(); 40 | bool backward(); 41 | 42 | void dump(std::ostream&, int depth); 43 | 44 | private: 45 | /// Whether or not the current focus is actually on any window 46 | bool m_currently_focused; 47 | 48 | /// The list of windows attached to this cycle 49 | std::list m_windows; 50 | 51 | /** 52 | * The currently focused window - note that this might be invalid if no 53 | * window is currently focused 54 | */ 55 | std::list::iterator m_current_focus; 56 | 57 | /// Whether or not there is a subcycle 58 | bool m_has_subcycle; 59 | 60 | /// Whether we are using our elements, or waiting on the subcycle 61 | bool m_subcycle_in_use; 62 | 63 | /// The currently attached subcycle, if any 64 | FocusCycle *m_subcycle; 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/model/screen.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "screen.h" 3 | 4 | /** 5 | * Finds out which screen a particular coordinate inhabits. 6 | */ 7 | Crt *CrtManager::screen_of_coord(Dimension x, Dimension y) const 8 | { 9 | for (std::map::const_iterator iter = m_boxes.cbegin(); 10 | iter != m_boxes.cend(); 11 | iter++) 12 | { 13 | const Box &value = iter->second; 14 | 15 | if (IN_BOUNDS(x, value.x, value.x + value.width) && 16 | IN_BOUNDS(y, value.y, value.y + value.height)) 17 | return iter->first; 18 | } 19 | 20 | return NULL; 21 | } 22 | 23 | /** 24 | * Finds the box of a particular screen. 25 | */ 26 | const Box &CrtManager::box_of_screen(Crt* screen) const 27 | { 28 | return m_boxes.find(screen)->second; 29 | } 30 | 31 | /** 32 | * Finds out which screen a box is representative of. 33 | * 34 | * This *could* be made more efficient by using a second std::map, but 35 | * there aren't enough screens that a linear search is prohibitively 36 | * expensive. 37 | */ 38 | Crt *CrtManager::screen_of_box(const Box &box) 39 | { 40 | for (std::map::iterator iter = m_boxes.begin(); 41 | iter != m_boxes.end(); 42 | iter++) 43 | { 44 | if (iter->second == box) 45 | return iter->first; 46 | } 47 | 48 | return NULL; 49 | } 50 | 51 | /** 52 | * Rebuilds the screen graph, from a list of screen bounding boxes. 53 | */ 54 | void CrtManager::rebuild_graph(std::vector &screens) 55 | { 56 | // The destructor for Crt traverses the entire graph 57 | delete m_root; 58 | m_boxes.clear(); 59 | 60 | // Make sure that each box is accessible by its root coordinates 61 | std::map origin_to_box; 62 | for (std::vector::iterator iter = screens.begin(); 63 | iter != screens.end(); 64 | iter++) 65 | origin_to_box[Dimension2D(iter->x, iter->y)] = *iter; 66 | 67 | // Start building the screen hierarchy at (0, 0) - the root screen 68 | m_root = new Crt(0); 69 | m_boxes[m_root] = origin_to_box[Dimension2D(0, 0)]; 70 | 71 | build_node(m_root, origin_to_box, 1); 72 | } 73 | 74 | /** 75 | * Converts all the information about the screen geometry to a textual 76 | * representation, which is written to the output stream. 77 | */ 78 | void CrtManager::dump(std::ostream &output) 79 | { 80 | output << "Screens\n"; 81 | 82 | std::map crts_by_id; 83 | build_id_map(m_root, crts_by_id); 84 | for (int id = 0; id < crts_by_id.size(); id++) 85 | { 86 | Crt *crt = crts_by_id[id]; 87 | Box &bounds = m_boxes[crt]; 88 | 89 | output << " Screen " << std::dec << id << "\n"; 90 | output << " " << bounds << "\n"; 91 | } 92 | } 93 | 94 | 95 | /** 96 | * Builds up the screen graph starting from a particular screen. 97 | * 98 | * Note that this goes only down and to the right - this is because the starting 99 | * point is intended to be the root screen at (0, 0). 100 | */ 101 | int CrtManager::build_node(Crt *screen, std::map &boxes, int next_id) 102 | { 103 | Box &my_box = m_boxes[screen]; 104 | 105 | Dimension2D below_box(my_box.x, my_box.y + my_box.height); 106 | Dimension2D right_box(my_box.x + my_box.width, my_box.y); 107 | 108 | if (boxes.count(below_box)) 109 | { 110 | Box &complete_below_box = boxes[below_box]; 111 | 112 | Crt *below = screen_of_box(complete_below_box); 113 | if (!below) 114 | { 115 | below = new Crt(next_id++); 116 | m_boxes[below] = complete_below_box; 117 | } 118 | 119 | screen->bottom = below; 120 | below->top = screen; 121 | 122 | next_id = build_node(below, boxes, next_id); 123 | } 124 | 125 | if (boxes.count(right_box)) 126 | { 127 | Box &complete_right_box = boxes[right_box]; 128 | 129 | Crt *right = screen_of_box(Box(complete_right_box)); 130 | if (!right) 131 | { 132 | right = new Crt(next_id++); 133 | m_boxes[right] = complete_right_box; 134 | } 135 | 136 | screen->right = right; 137 | right->left = screen; 138 | 139 | next_id = build_node(right, boxes, next_id); 140 | } 141 | 142 | return next_id; 143 | } 144 | 145 | /** 146 | * Builds up a map that relates each Crt to its ID. Used only for dumping 147 | * purposes. 148 | */ 149 | void CrtManager::build_id_map(Crt *base, std::map &id_map) 150 | { 151 | id_map[base->id] = base; 152 | 153 | if (base->right) 154 | build_id_map(base->right, id_map); 155 | 156 | if (base->bottom) 157 | build_id_map(base->bottom, id_map); 158 | } 159 | -------------------------------------------------------------------------------- /src/model/screen.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_SCREEN_MODEL__ 3 | #define __SMALLWM_SCREEN_MODEL__ 4 | 5 | #include "common.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Note - I would have *preferred* to call these things Screen* rather than Crtc*, 13 | // but XLib defines a Screen type already. I might introduce a SmallWM namespace 14 | // later to clear this up, but Crt will work for now. 15 | 16 | /** 17 | * Crts represent a graph of objects. They are used as keys in a map, so 18 | * they don't actually contain any data. 19 | * 20 | * Note that these are ephemeral - any time there is a change of screens, all 21 | * windows should update their current screen. Thankfully, however, screens 22 | * don't change themselves all the time. 23 | */ 24 | struct Crt { 25 | Crt *left, *right, *top, *bottom; 26 | 27 | /** 28 | * An identifier assigned to the screen, used only for dumped output 29 | * 30 | * The only rule is that this must be least on the root screen, and 31 | * greater on values away from the root - this ensures a readable ordering 32 | * when dumping. 33 | */ 34 | int id; 35 | 36 | Crt(int _id) : 37 | left(NULL), right(NULL), top(NULL), bottom(NULL), id(_id), 38 | m_deleting(false) 39 | {} 40 | 41 | ~Crt() 42 | { 43 | m_deleting = true; 44 | 45 | // Note that we have to break all links when deleting the screen, since 46 | // we don't want to get trapped in a cycle 47 | if (left && !left->m_deleting) 48 | { 49 | left->right = NULL; 50 | delete left; 51 | } 52 | if (right && !right->m_deleting) 53 | { 54 | right->left = NULL; 55 | delete right; 56 | } 57 | if (top && !top->m_deleting) 58 | { 59 | top->bottom = NULL; 60 | delete top; 61 | } 62 | if (bottom && !bottom->m_deleting) 63 | { 64 | bottom->top = NULL; 65 | delete bottom; 66 | } 67 | } 68 | 69 | private: 70 | /// This is used to avoid cycles when deleting a Crt 71 | bool m_deleting; 72 | }; 73 | 74 | /** 75 | * This captures the graph of screens, mapping each screen to its bonding box, 76 | * and allows for searching among the screens. 77 | */ 78 | class CrtManager { 79 | public: 80 | CrtManager() : m_root(NULL) 81 | {} 82 | 83 | ~CrtManager() 84 | { delete m_root; } 85 | 86 | Crt *root() const 87 | { return m_root; } 88 | 89 | Crt *screen_of_coord(Dimension, Dimension) const; 90 | const Box &box_of_screen(Crt*) const; 91 | Crt *screen_of_box(const Box &box); 92 | 93 | void rebuild_graph(std::vector&); 94 | 95 | void dump(std::ostream&); 96 | 97 | private: 98 | int build_node(Crt*, std::map&, int); 99 | void build_id_map(Crt*, std::map&); 100 | 101 | /// The root screen is located at (0, 0). Guaranteed not to be NULL 102 | Crt *m_root; 103 | 104 | /// The bounding box of each screen 105 | std::map m_boxes; 106 | }; 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/model/unique-multimap.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_UNIQUE_MULTIMAP__ 3 | #define __SMALLWM_UNIQUE_MULTIMAP__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /** 11 | * Think of a 2-layer tree: 12 | * 13 | * @code 14 | * * 15 | * __________|___________ 16 | * / | | \ 17 | * C1 C2 C3 C4 18 | * __|___ | / \ 19 | * / | \ | / \ 20 | * M1 M2 M3 M4 M5 M6 21 | * @endcode 22 | * 23 | * This two-level tree has the following structure: 24 | * 25 | * - * is the root, which is the UniqueMultimap itself. 26 | * - Cn is a category. Categories make up the second level of the tree, and 27 | * must be unique across the whole of UniqueMultimap. Categories can 28 | * _only_ appear at this level, and this whole level is made up exclusively 29 | * of categories. 30 | * - Mn is a member. Members make up the third level of a tree, and must 31 | * belong to one category - no more and no less. Members can _only_ appear 32 | * on this level, and this whole level is made up exclusively of members. 33 | * Members are unique to the UniqueMultimap - that is, no member 34 | * can belong to more than one category. 35 | */ 36 | template > 37 | class UniqueMultimap 38 | { 39 | public: 40 | typedef typename std::vector::const_iterator member_iter; 41 | 42 | /** 43 | * Returns whether or not a category value has a category in this object. 44 | */ 45 | bool is_category(category_t const &category) 46 | { 47 | return m_category_members.count(category) == 1; 48 | } 49 | 50 | /** 51 | * Returns whether or not a member is in this object. 52 | */ 53 | bool is_member(member_t const &member) 54 | { 55 | return m_member_to_category.count(member) == 1; 56 | } 57 | 58 | /** 59 | * Adds a new category with no elements. 60 | * 61 | * @param[in] category The category to insert. 62 | * @return Whether (true) or not (false) the insertion was successful. 63 | */ 64 | bool add_category(category_t const &category) 65 | { 66 | // Avoid including duplicates 67 | if (is_category(category)) 68 | return false; 69 | 70 | m_category_members[category] = new std::vector(); 71 | return true; 72 | } 73 | 74 | /** 75 | * Gets the category of a particular element. Note that the return value 76 | * is undefined if the element does not exist. 77 | * @param[in] element The element to find the category of. 78 | * @return The category. 79 | */ 80 | category_t &get_category_of(member_t const &element) 81 | { 82 | return m_member_to_category[element]; 83 | } 84 | 85 | /** 86 | * The starting iterator for the elements of a category. 87 | * @param[in] category The category to get the elements of. 88 | */ 89 | member_iter get_members_of_begin(category_t const &category) 90 | { 91 | return m_category_members[category]->begin(); 92 | } 93 | 94 | /** 95 | * The starting iterator for the elements of a category. 96 | * @param[in] category The category to get the elements of. 97 | */ 98 | member_iter get_members_of_end(category_t const &category) 99 | { 100 | return m_category_members[category]->end(); 101 | } 102 | 103 | /** 104 | * Gets the number of elements of a particular category. 105 | * @param[in] category The category to get the number of elements of. 106 | * @return The number of elements in a category. 107 | */ 108 | size_t count_members_of(category_t const &category) 109 | { 110 | return m_category_members[category]->size(); 111 | } 112 | 113 | /** 114 | * Adds a new element to an existing category. 115 | * @param[in] category The category of the new element. 116 | * @param[in] element The new element to add. 117 | * @return Whether (true) or not (false) the addition was successful. 118 | */ 119 | bool add_member(category_t const &category, member_t const &member) 120 | { 121 | if (is_member(member) || !is_category(category)) 122 | return false; 123 | 124 | m_category_members[category]->push_back(member); 125 | m_member_to_category[member] = category; 126 | return true; 127 | } 128 | 129 | /** 130 | * Moves an element from one category to another. 131 | * @param[in] element The element to move. 132 | * @param[in] new_category The category to move the element to. 133 | * @return Whether (true) or not (false) the move was successful. 134 | */ 135 | bool move_member(member_t const &member, category_t const &new_category) 136 | { 137 | if (!is_member(member) || !is_category(new_category)) 138 | return false; 139 | 140 | remove_member(member); 141 | add_member(new_category, member); 142 | return true; 143 | } 144 | 145 | /** 146 | * Removes an element from its current category. 147 | * @param[in] element The element to remove. 148 | * @return Whether (true) or not (false) the removal was successful. 149 | */ 150 | bool remove_member(member_t const &member) 151 | { 152 | if (!is_member(member)) 153 | return false; 154 | 155 | category_t const &old_category = m_member_to_category[member]; 156 | typename std::vector::iterator member_location = std::find( 157 | m_category_members[old_category]->begin(), 158 | m_category_members[old_category]->end(), 159 | member 160 | ); 161 | 162 | m_category_members[old_category]->erase(member_location); 163 | m_member_to_category.erase(member); 164 | return true; 165 | } 166 | private: 167 | /// The 'top-down' mapping from categories to their members 168 | std::map*, category_comparator_t> m_category_members; 169 | /// The 'bottom-up' mapping from members to their categories 170 | std::map m_member_to_category; 171 | }; 172 | 173 | /** 174 | * Sorts according to the categories of values contained inside of a 175 | * UniqueMultimap. 176 | */ 177 | template 178 | class UniqueMultimapSorter 179 | { 180 | public: 181 | UniqueMultimapSorter(UniqueMultimap &data) : 182 | m_uniquemultimap(data) 183 | {}; 184 | 185 | /** 186 | * Returns if A < B. 187 | */ 188 | bool operator()(member_t const &a, member_t const &b) 189 | { 190 | const category_t &a_category = m_uniquemultimap.get_category_of(a); 191 | const category_t &b_category = m_uniquemultimap.get_category_of(b); 192 | return a_category < b_category; 193 | } 194 | 195 | private: 196 | UniqueMultimap m_uniquemultimap; 197 | }; 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /src/model/x-model.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "x-model.h" 3 | 4 | /** 5 | * Registers a new icon - note that, at this point, XModel takes 6 | * responsibility for the given icon, and it should not be deleted unless 7 | * explicitly unregistered. 8 | * 9 | * @param icon The new icon to register. 10 | */ 11 | void XModel::register_icon(Icon *icon) 12 | { 13 | m_clients_to_icons[icon->client] = icon; 14 | m_icon_windows_to_icons[icon->icon] = icon; 15 | } 16 | 17 | /** 18 | * Unregisters an icon. This also removes the responsibility of the pointer 19 | * from the XModel, and it should be deleted later to avoid a memory leak. 20 | * 21 | * @param icon The icon to unregister. 22 | */ 23 | void XModel::unregister_icon(Icon *icon) 24 | { 25 | m_clients_to_icons.erase(icon->client); 26 | m_icon_windows_to_icons.erase(icon->icon); 27 | } 28 | 29 | /** 30 | * Gets the icon from the client window the icon is hiding. 31 | */ 32 | Icon* XModel::find_icon_from_client(Window client) const 33 | { 34 | if (m_clients_to_icons.count(client) == 0) 35 | return NULL; 36 | else 37 | return m_clients_to_icons.find(client)->second; 38 | } 39 | 40 | /** 41 | * Gets the icon from the icon window which is being shown. 42 | */ 43 | Icon* XModel::find_icon_from_icon_window(Window icon_win) const 44 | { 45 | if (m_icon_windows_to_icons.count(icon_win) == 0) 46 | return NULL; 47 | else 48 | return m_icon_windows_to_icons.find(icon_win)->second; 49 | } 50 | 51 | /** 52 | * Gets a list of all of the icons. 53 | */ 54 | void XModel::get_icons(std::vector &icons) 55 | { 56 | for (std::map::iterator iter = m_clients_to_icons.begin(); 57 | iter != m_clients_to_icons.end(); 58 | iter++) 59 | { 60 | if (iter->second != 0) 61 | icons.push_back(iter->second); 62 | } 63 | } 64 | 65 | /** 66 | * Registers that a client is being moved, recording the client and the 67 | * placeholder, and recording the current pointer location. 68 | * 69 | * @return true if the change is successful, false if the change cannot be 70 | * done due to an invalid state. 71 | */ 72 | void XModel::enter_move(Window client, Window placeholder, 73 | Dimension2D pointer) 74 | { 75 | if (m_moveresize) 76 | return; 77 | 78 | m_moveresize = new MoveResize(client, placeholder, MR_MOVE); 79 | m_pointer = pointer; 80 | } 81 | 82 | /** 83 | * Registers that a client is being resized, recording the client and the 84 | * placeholder, and recording the current pointer location. 85 | * 86 | * @return true if the change is successful, false if the change cannot be 87 | * done due to an invalid state. 88 | */ 89 | void XModel::enter_resize(Window client, Window placeholder, 90 | Dimension2D pointer) 91 | { 92 | if (m_moveresize) 93 | return; 94 | 95 | m_moveresize = new MoveResize(client, placeholder, MR_RESIZE); 96 | m_pointer = pointer; 97 | } 98 | 99 | /** 100 | * Updates the pointer to a new location, returning the difference between 101 | * the old position and the current position. 102 | * 103 | * Note that, if no movement or resizing is currently going on, then the 104 | * return value will be (0, 0). 105 | */ 106 | Dimension2D XModel::update_pointer(Dimension x, Dimension y) 107 | { 108 | if (!m_moveresize) 109 | return Dimension2D(0, 0); 110 | 111 | Dimension2D diff(x - DIM2D_X(m_pointer), 112 | y - DIM2D_Y(m_pointer)); 113 | 114 | DIM2D_X(m_pointer) = x; 115 | DIM2D_Y(m_pointer) = y; 116 | return diff; 117 | } 118 | 119 | /** 120 | * Gets the current placeholder which is being used to move/resize. 121 | * 122 | * @return The placeholder, or None if no window is being moved/resized. 123 | */ 124 | Window XModel::get_move_resize_placeholder() const 125 | { 126 | if (!m_moveresize) 127 | return None; 128 | 129 | return m_moveresize->placeholder; 130 | } 131 | 132 | /** 133 | * Gets the current client which is being moved/resized. 134 | * 135 | * @return The client, or None if no window is being moved/resized. 136 | */ 137 | Window XModel::get_move_resize_client() const 138 | { 139 | if (!m_moveresize) 140 | return None; 141 | 142 | return m_moveresize->client; 143 | } 144 | 145 | /** 146 | * Gets the current move/resize state. 147 | */ 148 | MoveResizeState XModel::get_move_resize_state() const 149 | { 150 | if (!m_moveresize) 151 | return MR_INVALID; 152 | 153 | return m_moveresize->state; 154 | } 155 | 156 | /** 157 | * Stops moving/resizing. 158 | */ 159 | void XModel::exit_move_resize() 160 | { 161 | if (!m_moveresize) 162 | return; 163 | 164 | delete m_moveresize; 165 | m_moveresize = 0; 166 | } 167 | 168 | /** 169 | * Checks to see if a window has the given effect flag, without changing it. 170 | */ 171 | bool XModel::has_effect(Window client, ClientEffect effect) 172 | { 173 | // Avoid creating an extra entry in the map for 174 | if (m_effects.count(client) == 0) return false; 175 | return m_effects[client] & effect != 0; 176 | } 177 | 178 | /** 179 | * Sets an effect flag on the given window. 180 | */ 181 | void XModel::set_effect(Window client, ClientEffect effect) 182 | { 183 | m_effects[client] = static_cast(m_effects[client] | effect); 184 | } 185 | 186 | /** 187 | * Unsets an effect flag on the given window. 188 | */ 189 | void XModel::clear_effect(Window client, ClientEffect effect) 190 | { 191 | m_effects[client] = static_cast(m_effects[client] | effect); 192 | } 193 | 194 | /** 195 | * Removes all effects from the given window. 196 | */ 197 | void XModel::remove_all_effects(Window client) 198 | { 199 | m_effects.erase(client); 200 | } 201 | -------------------------------------------------------------------------------- /src/model/x-model.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_X_MODEL__ 3 | #define __SMALLWM_X_MODEL__ 4 | #include 5 | #include 6 | 7 | #include "common.h" 8 | #include "xdata.h" 9 | 10 | /** 11 | * Stores the data necessary to handle an icon. 12 | */ 13 | struct Icon 14 | { 15 | Icon(Window _client, Window _icon, XGC *_gc) : 16 | client(_client), icon(_icon), gc(_gc) 17 | {}; 18 | 19 | /// The window that the icon "stands for" 20 | Window client; 21 | 22 | /// The icon window itself 23 | Window icon; 24 | 25 | /// The graphical context used to draw the icon 26 | XGC *gc; 27 | }; 28 | 29 | /** 30 | * The state of the client which is currently being moved or resized. 31 | */ 32 | enum MoveResizeState 33 | { 34 | MR_INVALID = 0, 35 | MR_MOVE, 36 | MR_RESIZE, 37 | }; 38 | 39 | /** 40 | * Stores the data necessary to move or resize a window. 41 | */ 42 | struct MoveResize 43 | { 44 | MoveResize(Window _client, Window _placeholder, MoveResizeState _state) : 45 | client(_client), placeholder(_placeholder), state(_state) 46 | {}; 47 | 48 | /// If this data is for a mover or a resizer 49 | MoveResizeState state; 50 | 51 | /// The placeholder window 52 | Window placeholder; 53 | 54 | /// The moved/resized client itself 55 | Window client; 56 | }; 57 | 58 | /** 59 | * A ClientEffect communicates to the X event handler that certain types of 60 | * events are expected, and that their usual processing can be ignored. 61 | */ 62 | enum ClientEffect { 63 | EXPECT_MAP = 1 << 0, 64 | EXPECT_UNMAP = 1 << 1 65 | }; 66 | 67 | 68 | /** 69 | * A data store for information about the UI of the window manager (rather than 70 | * information about the windows which are being managed). 71 | */ 72 | class XModel 73 | { 74 | public: 75 | XModel() : m_moveresize(0) 76 | {}; 77 | 78 | void register_icon(Icon*); 79 | void unregister_icon(Icon*); 80 | 81 | Icon *find_icon_from_client(Window) const; 82 | Icon *find_icon_from_icon_window(Window) const; 83 | void get_icons(std::vector&); 84 | 85 | void enter_move(Window, Window, Dimension2D); 86 | void enter_resize(Window, Window, Dimension2D); 87 | 88 | Dimension2D update_pointer(Dimension, Dimension); 89 | 90 | Window get_move_resize_placeholder() const; 91 | Window get_move_resize_client() const; 92 | MoveResizeState get_move_resize_state() const; 93 | 94 | void exit_move_resize(); 95 | 96 | bool has_effect(Window, ClientEffect); 97 | void set_effect(Window, ClientEffect); 98 | void clear_effect(Window, ClientEffect); 99 | void remove_all_effects(Window); 100 | 101 | private: 102 | /// A mapping between clients and their icons 103 | std::map m_clients_to_icons; 104 | 105 | /// A mapping between icon windows and the icon structures 106 | std::map m_icon_windows_to_icons; 107 | 108 | /// The effects present on each window 109 | std::map m_effects; 110 | 111 | /// The current data about moving or resizing 112 | MoveResize *m_moveresize; 113 | 114 | /// The current pointer location 115 | Dimension2D m_pointer; 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /src/smallwm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "actions.h" 8 | #include "clientmodel-events.h" 9 | #include "configparse.h" 10 | #include "common.h" 11 | #include "logging/logging.h" 12 | #include "logging/file.h" 13 | #include "logging/syslog.h" 14 | #include "model/changes.h" 15 | #include "model/client-model.h" 16 | #include "model/screen.h" 17 | #include "model/x-model.h" 18 | #include "xdata.h" 19 | #include "x-events.h" 20 | 21 | bool should_execute_dump = false; 22 | 23 | /** 24 | * Triggers a model state dump after the current batch of events has been processed. 25 | */ 26 | void enable_dump(int signal) 27 | { 28 | should_execute_dump = true; 29 | } 30 | 31 | /** 32 | * Prints out X errors to enable diagnosis, but doesn't kill us. 33 | * @param display The display the error occurred on 34 | * @param event The error event that happened 35 | */ 36 | int x_error_handler(Display *display, XErrorEvent *event) 37 | { 38 | char err_desc[500]; 39 | XGetErrorText(display, event->error_code, err_desc, 500); 40 | 41 | std::cerr << "X Error: \n" 42 | "\tdisplay = " << XDisplayName(NULL) << "'\n" 43 | "\tserial = " << event->serial << "'\n" 44 | "\terror = '" << err_desc << "\n" 45 | "\trequest = " << static_cast(event->request_code) << "\n" 46 | "\tminor = " << static_cast(event->minor_code) << "\n"; 47 | 48 | return 0; 49 | } 50 | 51 | int main() 52 | { 53 | // Make sure that child processes don't generate zombies. This is an 54 | // alternative to the wait() reaping loop under POSIX 2001 55 | signal(SIGCHLD, SIG_IGN); 56 | 57 | XSetErrorHandler(x_error_handler); 58 | signal(SIGUSR1, enable_dump); 59 | 60 | WMConfig config; 61 | config.load(); 62 | 63 | Log* logger; 64 | if (config.log_file == std::string("syslog")) 65 | { 66 | SysLog *sys_logger = new SysLog(); 67 | sys_logger->set_identity("SmallWM"); 68 | sys_logger->set_facility(LOG_USER); 69 | sys_logger->set_log_mask(LOG_UPTO(config.log_mask)); 70 | sys_logger->start(); 71 | logger = sys_logger; 72 | } 73 | else 74 | { 75 | logger = new FileLog(config.log_file, config.log_mask); 76 | } 77 | 78 | Display *display = XOpenDisplay(NULL); 79 | if (!display) 80 | { 81 | logger->log(LOG_ERR) << 82 | "Could not open X display - terminating" << Log::endl; 83 | logger->stop(); 84 | delete logger; 85 | 86 | std::exit(2); 87 | } 88 | 89 | Window default_root = DefaultRootWindow(display); 90 | XData xdata(*logger, display, default_root, DefaultScreen(display)); 91 | xdata.select_input(default_root, 92 | PointerMotionMask | 93 | StructureNotifyMask | 94 | SubstructureNotifyMask | 95 | SubstructureRedirectMask); 96 | 97 | CrtManager crt_manager; 98 | std::vector screens; 99 | xdata.get_screen_boxes(screens); 100 | crt_manager.rebuild_graph(screens); 101 | 102 | ChangeStream changes; 103 | ClientModel clients(changes, crt_manager, config.num_desktops, config.border_width); 104 | 105 | std::vector existing_windows; 106 | xdata.get_windows(existing_windows); 107 | 108 | XModel xmodel; 109 | XEvents x_events(config, xdata, clients, xmodel); 110 | 111 | for (std::vector::iterator win_iter = existing_windows.begin(); 112 | win_iter != existing_windows.end(); 113 | win_iter++) 114 | { 115 | if (*win_iter != default_root) 116 | x_events.add_window(*win_iter); 117 | } 118 | 119 | 120 | ClientModelEvents client_events(config, *logger, changes, 121 | xdata, clients, xmodel); 122 | 123 | // Make sure to process all the changes produced by the class actions for 124 | // the first set of windows 125 | client_events.handle_queued_changes(); 126 | 127 | while (x_events.step()) 128 | { 129 | if (should_execute_dump) 130 | { 131 | should_execute_dump = false; 132 | 133 | logger->log(LOG_NOTICE) << 134 | "Executing dump to target file '" << config.dump_file << 135 | "'" << Log::endl; 136 | 137 | std::fstream dump_file(config.dump_file.c_str(), 138 | std::fstream::out | std::fstream::app); 139 | 140 | if (dump_file) 141 | { 142 | dump_file << "#BEGIN DUMP\n"; 143 | crt_manager.dump(dump_file); 144 | clients.dump(dump_file); 145 | dump_file << "#END DUMP\n"; 146 | dump_file.close(); 147 | } 148 | else 149 | { 150 | logger->log(LOG_ERR) << 151 | "Could not open dump file '" << config.dump_file << 152 | "' for writing" << Log::endl; 153 | } 154 | } 155 | 156 | client_events.handle_queued_changes(); 157 | } 158 | 159 | logger->stop(); 160 | delete logger; 161 | 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #include "utils.h" 3 | 4 | /** 5 | * Removes characters from a string, irrespective of position. 6 | * 7 | * @param text The text to remove from. 8 | * @param remove A list of all characters to remove from the text. 9 | * @param buffer The location to put the output (must be at least as long as the input). 10 | */ 11 | void strip_string(const char *text, const char *remove, char *buffer) 12 | { 13 | char *buffer_iter = buffer; 14 | 15 | int idx; 16 | for (idx = 0; text[idx] != '\0'; idx++) 17 | { 18 | if (std::strchr(remove, text[idx]) == NULL) 19 | { 20 | *buffer_iter = text[idx]; 21 | buffer_iter++; 22 | } 23 | } 24 | 25 | // Ensure that the NUL terminator comes after all the content 26 | *buffer_iter = '\0'; 27 | } 28 | 29 | /** 30 | * Tries to convert a string to an unsigned integer. 31 | * 32 | * @param[in] string The text to convert. 33 | * @param default_ The default value to return if there is an error. 34 | * @return The parsed value, or the default. 35 | */ 36 | unsigned long try_parse_ulong(const char *string, unsigned long default_) 37 | { 38 | // strtoul is a little weird about negatives, and thus we have to filter 39 | // them out beforehand. 40 | if (strchr(string, '-')) 41 | return default_; 42 | 43 | // Since we have to make sure that the ending value indicates the end of the 44 | // value (instead of a non-numeric character), we have to strip the spaces 45 | // from the string. 46 | char *buffer = new char[strlen(string)]; 47 | strip_string(string, " \f\t\r\n\v", buffer); 48 | 49 | char *end_of_string; 50 | unsigned long result = strtoul(buffer, &end_of_string, 0); 51 | 52 | 53 | if (*end_of_string != '\0') 54 | result = default_; 55 | 56 | // This has to happen, since GCC causes the buffer to be cleared to zeroes. 57 | // 58 | // Since we use a comparison to zero when figuring out whether or not our 59 | // conversion succeeded, we have to wait until here to get rid of the 60 | // buffer. 61 | delete[] buffer; 62 | return result; 63 | } 64 | 65 | /** 66 | * Tries to convert a string to an unsigned integer, returning the default if 67 | * the input is equal to 0. 68 | * 69 | * @param[in] string The text to convert. 70 | * @param default_ The default value to return if there is an error. 71 | * @return The parsed value, or the default. 72 | */ 73 | unsigned long try_parse_ulong_nonzero(const char *string, unsigned long default_) 74 | { 75 | unsigned long result = try_parse_ulong(string, default_); 76 | if (result == 0) 77 | return default_; 78 | else 79 | return result; 80 | } 81 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_UTILS__ 3 | #define __SMALLWM_UTILS__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | unsigned long try_parse_ulong(const char *string, unsigned long default_); 11 | unsigned long try_parse_ulong_nonzero(const char *string, unsigned long default_); 12 | void strip_string(const char *text, const char *remove, char *buffer); 13 | 14 | template 15 | bool contains(InputIt start, InputIt end, T& value) 16 | { 17 | InputIt result = std::find(start, end, value); 18 | return result != end; 19 | } 20 | 21 | /** 22 | * A sorter that uses the elements being sorted as keys to a map, which are 23 | * sorted by their values in the map. 24 | */ 25 | template 26 | class MapSorter 27 | { 28 | public: 29 | MapSorter(std::map map): m_map(map) {} 30 | 31 | bool operator()(const Key &a, const Key &b) 32 | { 33 | return m_map[a] < m_map[b]; 34 | } 35 | 36 | private: 37 | std::map m_map; 38 | }; 39 | #endif 40 | -------------------------------------------------------------------------------- /src/x-events.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_X_EVENTS__ 3 | #define __SMALLWM_X_EVENTS__ 4 | 5 | #include 6 | 7 | #include "model/client-model.h" 8 | #include "model/x-model.h" 9 | #include "configparse.h" 10 | #include "common.h" 11 | #include "utils.h" 12 | #include "xdata.h" 13 | 14 | /** 15 | * A dispatcher for handling the different type of X events. 16 | * 17 | * This serves as the linkage between raw Xlib events, and changes in the 18 | * client model. 19 | */ 20 | class XEvents 21 | { 22 | public: 23 | XEvents(WMConfig &config, XData &xdata, ClientModel &clients, 24 | XModel &xmodel) : 25 | m_config(config), m_xdata(xdata), m_clients(clients), 26 | m_xmodel(xmodel), m_done(false) 27 | { 28 | xdata.add_hotkey_mouse(MOVE_BUTTON); 29 | xdata.add_hotkey_mouse(RESIZE_BUTTON); 30 | xdata.add_hotkey_mouse(LAUNCH_BUTTON); 31 | 32 | KeyboardAction actions[] = { 33 | CLIENT_NEXT_DESKTOP, CLIENT_PREV_DESKTOP, 34 | NEXT_DESKTOP, PREV_DESKTOP, 35 | TOGGLE_STICK, 36 | ICONIFY, 37 | MAXIMIZE, 38 | REQUEST_CLOSE, FORCE_CLOSE, 39 | K_SNAP_TOP, K_SNAP_BOTTOM, K_SNAP_LEFT, K_SNAP_RIGHT, 40 | SCREEN_TOP, SCREEN_BOTTOM, SCREEN_LEFT, SCREEN_RIGHT, 41 | LAYER_ABOVE, LAYER_BELOW, LAYER_TOP, LAYER_BOTTOM, 42 | LAYER_1, LAYER_2, LAYER_3, LAYER_4, LAYER_5, LAYER_6, LAYER_7, LAYER_8, LAYER_9, 43 | CYCLE_FOCUS, CYCLE_FOCUS_BACK, EXIT_WM, 44 | INVALID_ACTION 45 | }; 46 | 47 | for (KeyboardAction *action = &actions[0]; *action != INVALID_ACTION; action++) 48 | { 49 | KeyBinding &binding = config.key_commands.action_to_binding[*action]; 50 | xdata.add_hotkey(binding.first, binding.second); 51 | } 52 | }; 53 | 54 | bool step(); 55 | 56 | // Note that this is exposed because smallwm.cpp has to import existing 57 | // windows when main() runs 58 | void add_window(Window); 59 | 60 | private: 61 | void handle_rrnotify(); 62 | void handle_keypress(); 63 | void handle_buttonpress(); 64 | void handle_buttonrelease(); 65 | void handle_motionnotify(); 66 | void handle_configurenotify(); 67 | void handle_mapnotify(); 68 | void handle_unmapnotify(); 69 | void handle_expose(); 70 | void handle_destroynotify(); 71 | 72 | void handle_configurerequest(); 73 | void handle_maprequest(); 74 | void handle_circulaterequest(); 75 | 76 | /// The currently active event 77 | XEvent m_event; 78 | 79 | /// Whether or not the user has terminated SmallWM 80 | bool m_done; 81 | 82 | /// The configuration options that were given in the configuration file 83 | WMConfig &m_config; 84 | 85 | /// The data required to interface with Xlib 86 | XData &m_xdata; 87 | 88 | /// The data model which stores the clients and data about them 89 | ClientModel &m_clients; 90 | 91 | /** The data model which stores information related to clients, but not 92 | * about them. */ 93 | XModel &m_xmodel; 94 | 95 | /// The offset for all RandR generated events 96 | int m_randroffset; 97 | }; 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/xdata.h: -------------------------------------------------------------------------------- 1 | /** @file */ 2 | #ifndef __SMALLWM_XDATA__ 3 | #define __SMALLWM_XDATA__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common.h" 11 | #include "logging/logging.h" 12 | 13 | /** 14 | * An X graphics context which is used to draw on windows. 15 | */ 16 | class XGC 17 | { 18 | public: 19 | XGC(Display *dpy, Window window) : 20 | m_display(dpy), m_window(window) 21 | { 22 | m_gc = XCreateGC(dpy, window, 0, NULL); 23 | }; 24 | 25 | ~XGC() 26 | { 27 | XFree(m_gc); 28 | }; 29 | 30 | void clear(); 31 | void draw_string(Dimension, Dimension, const std::string&); 32 | Dimension2D copy_pixmap(Drawable, Dimension, Dimension); 33 | 34 | private: 35 | /** The raw X display - this is necessary to have since XData doesn't 36 | * expose it. */ 37 | Display *m_display; 38 | 39 | 40 | /// The window this graphics context belongs to 41 | Window m_window; 42 | 43 | /// The X graphics context this sits above 44 | GC m_gc; 45 | }; 46 | 47 | /** 48 | * Identifies the colors which can be used for window borders and the like. 49 | */ 50 | enum MonoColor 51 | { 52 | X_BLACK, 53 | X_WHITE, 54 | }; 55 | 56 | /** 57 | * This forms a layer above raw Xlib, which stores the X display, root 58 | * window, etc. and provides the most common operations which use these data. 59 | */ 60 | class XData 61 | { 62 | public: 63 | XData(Log &logger, Display *dpy, Window root, int screen) : 64 | m_display(dpy), m_logger(logger), m_confined(None), 65 | m_old_root_mask(NoEventMask), m_substructure_depth(0) 66 | { 67 | m_root = DefaultRootWindow(dpy); 68 | m_screen = DefaultScreen(dpy); 69 | 70 | init_xrandr(); 71 | load_modifier_flags(); 72 | }; 73 | 74 | void init_xrandr(); 75 | void load_modifier_flags(); 76 | 77 | XGC *create_gc(Window); 78 | Window create_window(bool); 79 | 80 | void change_property(Window, const std::string&, Atom, 81 | const unsigned char*, size_t); 82 | 83 | void next_event(XEvent&); 84 | void get_latest_event(XEvent&, int); 85 | 86 | void add_hotkey(KeySym, bool); 87 | void add_hotkey_mouse(unsigned int); 88 | 89 | void confine_pointer(Window); 90 | void stop_confining_pointer(); 91 | void grab_mouse(Window); 92 | void ungrab_mouse(Window); 93 | 94 | void select_input(Window, long); 95 | 96 | void get_windows(std::vector&); 97 | void get_pointer_location(Dimension&, Dimension&); 98 | 99 | Window get_input_focus(); 100 | bool set_input_focus(Window); 101 | 102 | void map_win(Window); 103 | void unmap_win(Window); 104 | void request_close(Window); 105 | void destroy_win(Window); 106 | 107 | void get_attributes(Window, XWindowAttributes&); 108 | void set_attributes(Window, XSetWindowAttributes&, 109 | unsigned long); 110 | bool is_mapped(Window); 111 | void set_border_color(Window, MonoColor); 112 | void set_border_width(Window, Dimension); 113 | 114 | void move_window(Window, Dimension, Dimension); 115 | void resize_window(Window, Dimension, Dimension); 116 | void raise(Window); 117 | void restack(const std::vector&); 118 | 119 | bool get_wm_hints(Window, XWMHints&); 120 | void get_size_hints(Window, XSizeHints&); 121 | Window get_transient_hint(Window); 122 | void get_icon_name(Window, std::string&); 123 | void get_class(Window, std::string&); 124 | 125 | void get_screen_boxes(std::vector&); 126 | 127 | KeySym get_keysym(int); 128 | void keysym_to_string(KeySym, std::string&); 129 | 130 | void forward_configure_request(XEvent&, unsigned int); 131 | void forward_circulate_request(XEvent&); 132 | 133 | /// The event code X adds to each XRandR event (used by XEvents) 134 | int randr_event_offset; 135 | 136 | unsigned int primary_mod_flag; 137 | unsigned int secondary_mod_flag; 138 | 139 | unsigned int num_mod_flag; 140 | unsigned int caps_mod_flag; 141 | unsigned int scroll_mod_flag; 142 | 143 | private: 144 | Atom intern_if_needed(const std::string&); 145 | unsigned long decode_monocolor(MonoColor); 146 | 147 | void enable_substructure_events(); 148 | void disable_substructure_events(); 149 | 150 | /** We save this to ensure that we can re-enable substructure events if they 151 | * were enabled before a call to disable_substructure_events. 152 | */ 153 | long m_old_root_mask; 154 | 155 | /// How deep we are inside of a nested group of enable/disable substruture events 156 | int m_substructure_depth; 157 | 158 | /// The logging interface 159 | Log &m_logger; 160 | 161 | /// The connection to the X server 162 | Display *m_display; 163 | 164 | /// The root window on the display 165 | Window m_root; 166 | 167 | /// The default X11 screen 168 | int m_screen; 169 | 170 | /// The pre-defined atoms, which are accessible via a string 171 | std::map m_atoms; 172 | 173 | /// The window the pointer is confined to, or None 174 | Window m_confined; 175 | }; 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /test/changes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "model/changes.h" 3 | 4 | Window win = 1; 5 | 6 | SUITE(ChangeStreamSuite) 7 | { 8 | TEST(test_is_empty_by_default) 9 | { 10 | ChangeStream stream; 11 | 12 | CHECK(!stream.has_more()); 13 | CHECK_EQUAL(stream.get_next(), static_cast(0)); 14 | } 15 | 16 | TEST(test_pushed_elems_can_be_read) 17 | { 18 | ChangeStream stream; 19 | ChangeSize const *change = new ChangeSize(win, 42, 42); 20 | 21 | stream.push(change); 22 | 23 | CHECK(stream.has_more()); 24 | CHECK_EQUAL(stream.get_next(), change); 25 | 26 | CHECK(!stream.has_more()); 27 | CHECK_EQUAL(stream.get_next(), static_cast(0)); 28 | } 29 | 30 | TEST(test_multiple_elems_retain_order) 31 | { 32 | ChangeStream stream; 33 | ChangeSize const *change1 = new ChangeSize(win, 42, 42); 34 | ChangeSize const *change2 = new ChangeSize(win, 21, 21); 35 | 36 | stream.push(change1); 37 | stream.push(change2); 38 | 39 | CHECK(stream.has_more()); 40 | CHECK_EQUAL(stream.get_next(), change1); 41 | 42 | CHECK(stream.has_more()); 43 | CHECK_EQUAL(stream.get_next(), change2); 44 | 45 | CHECK(!stream.has_more()); 46 | CHECK_EQUAL(stream.get_next(), static_cast(0)); 47 | } 48 | 49 | TEST(test_flush_clears_elems) 50 | { 51 | ChangeStream stream; 52 | ChangeSize const *change1 = new ChangeSize(win, 42, 42); 53 | ChangeSize const *change2 = new ChangeSize(win, 21, 21); 54 | 55 | stream.push(change1); 56 | stream.push(change2); 57 | 58 | stream.flush(); 59 | 60 | CHECK(!stream.has_more()); 61 | CHECK_EQUAL(stream.get_next(), static_cast(0)); 62 | } 63 | } 64 | 65 | int main() 66 | { 67 | return UnitTest::RunAllTests(); 68 | } 69 | -------------------------------------------------------------------------------- /test/screen.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "model/screen.h" 6 | 7 | // Useful for doing 'screen_of_coord' testing so that I can avoid repeating code 8 | struct CrtOfCoordTest 9 | { 10 | int x, y; 11 | Crt *expected_screen; 12 | }; 13 | 14 | void test_screen_expectations(CrtManager &mgr, CrtOfCoordTest *tests, int n_tests) 15 | { 16 | std::cout << "TESTING\n"; 17 | for (int i = 0; i < n_tests; i++) 18 | { 19 | CrtOfCoordTest test = tests[i]; 20 | std::cout << "\t(" << test.x << ", " << test.y << ") --> " << test.expected_screen << "\n"; 21 | CHECK_EQUAL(mgr.screen_of_coord(test.x, test.y), test.expected_screen); 22 | } 23 | } 24 | 25 | SUITE(CrtMemberSuite) 26 | { 27 | TEST(OnlyOneCrt) 28 | { 29 | /** 30 | * Ensures that a single-screen configuration has only the root 31 | * screen defined. 32 | */ 33 | CrtManager mgr; 34 | 35 | std::vector screens; 36 | screens.push_back(Box(0, 0, 1366, 768)); 37 | 38 | mgr.rebuild_graph(screens); 39 | 40 | // Ensure that: 41 | // 42 | // 1. There is one Crt, the root. 43 | // 2. It has the Box we expect. 44 | // 3. Some coords on the screen go back to the root screen. 45 | // 4. Some coords off the screen go back to static_cast(0) (no screen). 46 | Crt *root = mgr.root(); 47 | 48 | CHECK(root != static_cast(0)); 49 | CHECK_EQUAL(root->left, static_cast(0)); 50 | CHECK_EQUAL(root->right, static_cast(0)); 51 | CHECK_EQUAL(root->top, static_cast(0)); 52 | CHECK_EQUAL(root->bottom, static_cast(0)); 53 | 54 | CHECK_EQUAL(screens[0], mgr.box_of_screen(root)); 55 | 56 | CHECK_EQUAL(root, mgr.screen_of_box(screens[0])); 57 | 58 | CrtOfCoordTest tests[] = { 59 | // Check 3 types of inside points: 60 | // 1. The left edge. 61 | // 2. The top edge. 62 | // (Omit the right and bottom since those are the next monitor over) 63 | // 3. The inside. 64 | {0, 500, root}, 65 | {1000, 0, root}, 66 | {1000, 500, root}, 67 | 68 | // Check 4 types of outside points: 69 | // 1. The right edge. 70 | // 2. The bottom edge. 71 | // 3. Out in the right area. 72 | // 4. Out in the bottom area. 73 | {1366, 0, static_cast(0)}, 74 | {0, 768, static_cast(0)}, 75 | {1500, 500, static_cast(0)}, 76 | {500, 1500, static_cast(0)}, 77 | }; 78 | test_screen_expectations(mgr, tests, 7); 79 | } 80 | 81 | TEST(SingleCrtsToRightAndBottom) 82 | { 83 | /** 84 | * Ensure that screens other than the root are detected 85 | */ 86 | CrtManager mgr; 87 | 88 | std::vector screens; 89 | 90 | /* 91 | * +----------+---------+ 92 | * | 1500x500 | 500x500 | 93 | * | | | 94 | * +----------+---------+ 95 | * | 1500x500 | 96 | * | | 97 | * +----------+ 98 | */ 99 | screens.push_back(Box(0, 0, 1500, 500)); 100 | screens.push_back(Box(1500, 0, 500, 500)); 101 | screens.push_back(Box(0, 500, 1500, 500)); 102 | 103 | mgr.rebuild_graph(screens); 104 | 105 | // The root should have a bottom and right peer 106 | Crt *root = mgr.root(); 107 | Crt *right = root->right; 108 | Crt *bottom = root->bottom; 109 | 110 | // Check that the boundaries don't have anything beyond them 111 | CHECK_EQUAL(root->top, static_cast(0)); 112 | CHECK_EQUAL(root->left, static_cast(0)); 113 | CHECK_EQUAL(right->top, static_cast(0)); 114 | CHECK_EQUAL(right->right, static_cast(0)); 115 | CHECK_EQUAL(right->bottom, static_cast(0)); 116 | CHECK_EQUAL(bottom->left, static_cast(0)); 117 | CHECK_EQUAL(bottom->bottom, static_cast(0)); 118 | CHECK_EQUAL(bottom->right, static_cast(0)); 119 | 120 | // Checks the interlinks between the screens 121 | CHECK_EQUAL(bottom->top, root); 122 | CHECK_EQUAL(right->left, root); 123 | 124 | // Now check the screen boundaries 125 | CHECK_EQUAL(mgr.box_of_screen(root), screens[0]); 126 | CHECK_EQUAL(mgr.box_of_screen(right), screens[1]); 127 | CHECK_EQUAL(mgr.box_of_screen(bottom), screens[2]); 128 | 129 | CHECK_EQUAL(mgr.screen_of_box(screens[0]), root); 130 | CHECK_EQUAL(mgr.screen_of_box(screens[1]), right); 131 | CHECK_EQUAL(mgr.screen_of_box(screens[2]), bottom); 132 | 133 | CrtOfCoordTest tests[] = { 134 | // Finally, check the following coordinates and ensure that they 135 | // are on their right places: 136 | // 1. Far-left edge -> Root screen 137 | // 2. Far-top edge -> Root screen 138 | // 3. Vertical middle (between root and right) -> Right screen 139 | // 4. Horizontal middle (between root and bottom) -> Bottom screen 140 | // 5. Internal of the root screen 141 | // 6. Internal of the right screen 142 | // 7. Internal of the bottom screen 143 | {0, 250, root}, 144 | {500, 0, root}, 145 | {1500, 250, right}, 146 | {1000, 500, bottom}, 147 | {1000, 250, root}, 148 | {1750, 250, right}, 149 | {750, 750, bottom}, 150 | 151 | // Test a few external points as well 152 | // 1. Below the bottom 153 | // 2. Right of the right 154 | // 3. Right of the bottom and below the right 155 | {500, 1500, static_cast(0)}, 156 | {2500, 250, static_cast(0)}, 157 | {1750, 750, static_cast(0)}, 158 | }; 159 | test_screen_expectations(mgr, tests, 10); 160 | } 161 | } 162 | 163 | int main() 164 | { 165 | return UnitTest::RunAllTests(); 166 | } 167 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | TEST(Succeed) 4 | { 5 | CHECK(true); 6 | } 7 | 8 | int main() 9 | { 10 | return UnitTest::RunAllTests(); 11 | } 12 | -------------------------------------------------------------------------------- /test/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "utils.h" 5 | 6 | TEST(test_strip_string) 7 | { 8 | char buffer[100]; 9 | /* 10 | * This test data checks a number of things: 11 | * 12 | * 1. That data not part of the removal list is not removed. 13 | * 2. That data on the front, back and the internal of the text 14 | * is removed. 15 | */ 16 | strip_string(" \t\ntest text\t\n ", " \t\n", buffer); 17 | CHECK_EQUAL(std::strcmp(buffer, "testtext"), 0); 18 | } 19 | 20 | TEST(test_try_parse_ulong) 21 | { 22 | // Try a positive number in the long range 23 | CHECK_EQUAL(try_parse_ulong("123456", 0), 123456); 24 | 25 | // Try a positive number in an alternate base 26 | CHECK_EQUAL(try_parse_ulong("0x123", 0), 0x123); 27 | 28 | // Make sure negatives return the default 29 | CHECK_EQUAL(try_parse_ulong("-123", 0), 0); 30 | 31 | // Make sure that garbage returns the default 32 | CHECK_EQUAL(try_parse_ulong("asdfjkl;", 0), 0); 33 | } 34 | 35 | TEST(test_try_parse_ulong_nonzero) 36 | { 37 | // Try a positive number in the long range 38 | CHECK_EQUAL(try_parse_ulong_nonzero("123456", 0), 123456); 39 | 40 | // Try a positive number in an alternate base 41 | CHECK_EQUAL(try_parse_ulong_nonzero("0x123", 0), 0x123); 42 | 43 | // Make sure negatives return the default 44 | CHECK_EQUAL(try_parse_ulong_nonzero("-123", 0), 0); 45 | 46 | // Make sure that garbage returns the default 47 | CHECK_EQUAL(try_parse_ulong_nonzero("asdfjkl;", 0), 0); 48 | 49 | // Ensure that zero returns the default 50 | CHECK_EQUAL(try_parse_ulong_nonzero("0", 42), 42); 51 | } 52 | 53 | int main() 54 | { 55 | return UnitTest::RunAllTests(); 56 | } 57 | --------------------------------------------------------------------------------