1/* Self tests for the filtered_iterator class.
2
3   Copyright (C) 2019-2020 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "gdbsupport/common-defs.h"
21#include "gdbsupport/selftest.h"
22#include "gdbsupport/filtered-iterator.h"
23
24#include <iterator>
25
26namespace selftests {
27
28/* An iterator class that iterates on integer arrays.  */
29
30struct int_array_iterator
31{
32  using value_type = int;
33  using reference = int &;
34  using pointer = int *;
35  using iterator_category = std::forward_iterator_tag;
36  using difference_type = int;
37
38  /* Create an iterator that points at the first element of an integer
39     array at ARRAY of size SIZE.  */
40  int_array_iterator (int *array, size_t size)
41    : m_array (array), m_size (size)
42  {}
43
44  /* Create a past-the-end iterator.  */
45  int_array_iterator ()
46    : m_array (nullptr), m_size (0)
47  {}
48
49  bool operator== (const int_array_iterator &other) const
50  {
51    /* If both are past-the-end, they are equal.  */
52    if (m_array == nullptr && other.m_array == nullptr)
53      return true;
54
55    /* If just one of them is past-the-end, they are not equal.  */
56    if (m_array == nullptr || other.m_array == nullptr)
57      return false;
58
59    /* If they are both not past-the-end, make sure they iterate on the
60       same array (we shouldn't compare iterators that iterate on different
61       things).  */
62    gdb_assert (m_array == other.m_array);
63
64    /* They are equal if they have the same current index.  */
65    return m_cur_idx == other.m_cur_idx;
66  }
67
68  bool operator!= (const int_array_iterator &other) const
69  {
70    return !(*this == other);
71  }
72
73  void operator++ ()
74  {
75    /* Make sure nothing tries to increment a past the end iterator. */
76    gdb_assert (m_cur_idx < m_size);
77
78    m_cur_idx++;
79
80    /* Mark the iterator as "past-the-end" if we have reached the end.  */
81    if (m_cur_idx == m_size)
82      m_array = nullptr;
83  }
84
85  int operator* () const
86  {
87    /* Make sure nothing tries to dereference a past the end iterator.  */
88    gdb_assert (m_cur_idx < m_size);
89
90    return m_array[m_cur_idx];
91  }
92
93private:
94  /* A nullptr value in M_ARRAY indicates a past-the-end iterator.  */
95  int *m_array;
96  size_t m_size;
97  size_t m_cur_idx = 0;
98};
99
100/* Filter to only keep the even numbers.  */
101
102struct even_numbers_only
103{
104  bool operator() (int n)
105  {
106    return n % 2 == 0;
107  }
108};
109
110/* Test typical usage.  */
111
112static void
113test_filtered_iterator ()
114{
115  int array[] = { 4, 4, 5, 6, 7, 8, 9 };
116  std::vector<int> even_ints;
117  const std::vector<int> expected_even_ints { 4, 4, 6, 8 };
118
119  filtered_iterator<int_array_iterator, even_numbers_only>
120    iter (array, ARRAY_SIZE (array));
121  filtered_iterator<int_array_iterator, even_numbers_only> end;
122
123  for (; iter != end; ++iter)
124    even_ints.push_back (*iter);
125
126  gdb_assert (even_ints == expected_even_ints);
127}
128
129/* Test operator== and operator!=. */
130
131static void
132test_filtered_iterator_eq ()
133{
134  int array[] = { 4, 4, 5, 6, 7, 8, 9 };
135
136  filtered_iterator<int_array_iterator, even_numbers_only>
137    iter1(array, ARRAY_SIZE (array));
138  filtered_iterator<int_array_iterator, even_numbers_only>
139    iter2(array, ARRAY_SIZE (array));
140
141  /* They start equal.  */
142  gdb_assert (iter1 == iter2);
143  gdb_assert (!(iter1 != iter2));
144
145  /* Advance 1, now they aren't equal (despite pointing to equal values).  */
146  ++iter1;
147  gdb_assert (!(iter1 == iter2));
148  gdb_assert (iter1 != iter2);
149
150  /* Advance 2, now they are equal again.  */
151  ++iter2;
152  gdb_assert (iter1 == iter2);
153  gdb_assert (!(iter1 != iter2));
154}
155
156} /* namespace selftests */
157
158void _initialize_filtered_iterator_selftests ();
159void
160_initialize_filtered_iterator_selftests ()
161{
162  selftests::register_test ("filtered_iterator",
163			    selftests::test_filtered_iterator);
164  selftests::register_test ("filtered_iterator_eq",
165			    selftests::test_filtered_iterator_eq);
166}
167