1 // (C) Copyright Gennadiy Rozental 2001-2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5
6 // See http://www.boost.org/libs/test for the library home page.
7
8 // Boost.Test
9 #include <boost/test/unit_test.hpp>
10 #include <boost/test/utils/algorithm.hpp>
11 #include <boost/test/tools/floating_point_comparison.hpp>
12 #include <boost/test/parameterized_test.hpp>
13 using namespace boost::unit_test;
14
15 // BOOST
16 #include <boost/functional.hpp>
17 #include <boost/static_assert.hpp>
18 #include <boost/mem_fn.hpp>
19 #include <boost/bind/bind.hpp>
20
21 // STL
22 #include <string>
23 #include <stdexcept>
24 #include <algorithm>
25 #include <functional>
26 #include <iostream>
27 #include <memory>
28 #include <list>
29
30 //____________________________________________________________________________//
31
32 template<int n>
33 struct power_of_10 {
34 BOOST_STATIC_CONSTANT( unsigned long, value = 10*power_of_10<n-1>::value );
35 };
36
37 template<>
38 struct power_of_10<0> {
39 BOOST_STATIC_CONSTANT( unsigned long, value = 1 );
40 };
41
42 //____________________________________________________________________________//
43
44 template<int AlphabetSize>
45 class hash_function {
46 public:
47 BOOST_STATIC_ASSERT( AlphabetSize <= 5 );
48
hash_function(std::string const & alphabet)49 explicit hash_function( std::string const& alphabet )
50 : m_alphabet( alphabet )
51 {
52 if( m_alphabet.size() != AlphabetSize )
53 throw std::runtime_error( "Wrong alphabet size" );
54
55 std::sort( m_alphabet.begin(), m_alphabet.end() );
56
57 if( std::adjacent_find( m_alphabet.begin(), m_alphabet.end() ) != m_alphabet.end() )
58 throw std::logic_error( "Duplicate characters in alphabet" );
59 }
60
operator ()(std::string const & arg)61 unsigned long operator()( std::string const& arg )
62 {
63 m_result = 0;
64
65 if( arg.length() > 8 )
66 throw std::runtime_error( "Wrong argument size" );
67
68 std::string::const_iterator it = std::find_if( arg.begin(), arg.end(),
69 BOOST_TEST_BIND1ST( boost::mem_fun( &hash_function::helper_ ), this ) );
70
71 if( it != arg.end() )
72 throw std::out_of_range( std::string( "Invalid character " ) + *it );
73
74 return m_result;
75 }
76
77 private:
helper_(char c)78 bool helper_( char c )
79 {
80 std::string::const_iterator it = std::find( m_alphabet.begin(), m_alphabet.end(), c );
81
82 if( it == m_alphabet.end() )
83 return true;
84
85 m_result += power_of_10_( it - m_alphabet.begin() );
86
87 return false;
88 }
89
power_of_10_(int i)90 unsigned long power_of_10_( int i ) {
91 switch( i ) {
92 case 0: return power_of_10<0>::value;
93 case 1: return power_of_10<1>::value;
94 case 2: return power_of_10<2>::value;
95 case 3: return power_of_10<3>::value;
96 case 4: return power_of_10<4>::value;
97 default: return 0;
98 }
99 }
100
101 // Data members
102 std::string m_alphabet;
103 unsigned long m_result;
104 };
105
106 //____________________________________________________________________________//
107
108 struct hash_function_test_data {
109 std::string orig_string;
110 unsigned long exp_value;
111
operator >>(std::istream & istr,hash_function_test_data & test_data)112 friend std::istream& operator>>( std::istream& istr, hash_function_test_data& test_data )
113 {
114 std::istream& tmp = istr >> test_data.orig_string;
115 return !tmp ? tmp : istr >> test_data.exp_value;
116 }
117 };
118
119 //____________________________________________________________________________//
120
121 class hash_function_tester {
122 public:
hash_function_tester(std::string const & alphabet)123 explicit hash_function_tester( std::string const& alphabet )
124 : m_function_under_test( alphabet ) {}
125
test(hash_function_test_data const & test_data)126 void test( hash_function_test_data const& test_data )
127 {
128 if( test_data.exp_value == (unsigned long)-1 )
129 BOOST_CHECK_THROW( m_function_under_test( test_data.orig_string ), std::runtime_error );
130 else if( test_data.exp_value == (unsigned long)-2 )
131 BOOST_CHECK_THROW( m_function_under_test( test_data.orig_string ), std::out_of_range );
132 else {
133 BOOST_TEST_MESSAGE( "Testing: " << test_data.orig_string );
134 BOOST_CHECK_EQUAL( m_function_under_test( test_data.orig_string ), test_data.exp_value );
135 }
136 }
137
138 private:
139 hash_function<4> m_function_under_test;
140 };
141
142 //____________________________________________________________________________//
143
144 struct massive_hash_function_test : test_suite {
massive_hash_function_testmassive_hash_function_test145 massive_hash_function_test() : test_suite( "massive_hash_function_test" ) {
146 std::string alphabet;
147 std::cout << "Enter alphabet (4 characters without delimeters)\n";
148 std::cin >> alphabet;
149
150 boost::shared_ptr<hash_function_tester> instance( new hash_function_tester( alphabet ) );
151
152 std::cout << "\nEnter test data in a format [string] [value] to check correct calculation\n";
153 std::cout << "Enter test data in a format [string] -1 to check long string validation\n";
154 std::cout << "Enter test data in a format [string] -2 to check invalid argument string validation\n";
155
156 std::list<hash_function_test_data> test_data_store;
157
158 while( !std::cin.eof() ) {
159 hash_function_test_data test_data;
160
161 if( !(std::cin >> test_data) )
162 break;
163
164 test_data_store.push_back( test_data );
165 }
166
167 add( make_test_case( &hash_function_tester::test,
168 "hash_function_tester",
169 __FILE__,
170 __LINE__,
171 instance,
172 test_data_store.begin(),
173 test_data_store.end() ) );
174 }
175 };
176
177 //____________________________________________________________________________//
178
179 test_suite*
init_unit_test_suite(int,char * [])180 init_unit_test_suite( int, char* [] ) {
181 framework::master_test_suite().p_name.value = "Unit test example 12";
182
183 framework::master_test_suite().add( new massive_hash_function_test );
184
185 return 0;
186 }
187
188 //____________________________________________________________________________//
189
190 // EOF
191