summaryrefslogtreecommitdiff
path: root/src/generic/problemresolver/sanity_check_universe.h
blob: b0a1ea954fd996cba16ba5b56f5db9928d8cd8b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
// sanity_check_universe.h                  -*-c++-*-
//
//   Copyright (C) 2007-2009 Daniel Burrows
//
//   This program is free software; you can redistribute it and/or
//   modify it under the terms of the GNU General Public License as
//   published by the Free Software Foundation; either version 2 of
//   the License, or (at your option) any later version.
//
//   This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//   General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program; see the file COPYING.  If not, write to
//   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
//   Boston, MA 02111-1307, USA.

#include <iostream>
#include <set>

#include "problemresolver.h"
#include "solution.h"

/** \file sanity_check_universe.h */

/** Check that forward/reverse linkages in a package universe
 *  are correct, and spit errors to stdout if not.
 *
 *  Tests that:
 *    - For each package, each version of that package links
 *      back to the package.
 *    - For each package, the package's "current version" occurs
 *      in its version list.
 *    - For each version, the version is the source of all its
 *      forward dependencies.
 *    - For each version of each package, and each reverse
 *      dependency of that version, some version of that package
 *      occurs in the solvers list.
 *    - For each version of each package, each dependency and
 *      reverse dependency of the version occur in the global
 *      dependency list. (TODO: unimplemented)
 *    - Each dependency in the universe occurs in its
 *      source version's dependency list.
 *    - For each dependency in the universe, solved_by() returns
 *      \b true for all its solvers and all other versions of its
 *      source, and \b false for all other package versions.
 *    - Each dependency in the universe, for each of its solvers,
 *      appears either in that solver's reverse dependency list
 *      or in the reverse dependency list of EACH version of
 *      that same package that is not a solver of the dependency.
 *    - Each broken dependency is broken in an empty solution.
 */
template<typename PackageUniverse>
void sanity_check_universe(const PackageUniverse &universe)
{
  typedef typename PackageUniverse::package package;
  typedef typename PackageUniverse::version version;
  typedef typename PackageUniverse::dep dep;

  typedef typename PackageUniverse::package_iterator package_iterator;
  typedef typename PackageUniverse::dep_iterator dep_iterator;
  typedef typename PackageUniverse::broken_dep_iterator broken_dep_iterator;

  std::set<dep> seenRevDeps, seenDeps;
  std::set<package> seenPkgs;
  std::set<version> seenVers;

  for(package_iterator pIt = universe.packages_begin();
      !pIt.end(); ++pIt)
    {
      seenPkgs.insert(*pIt);

      for(typename package::version_iterator vIt = (*pIt).versions_begin();
	  !vIt.end(); ++vIt)
	{
	  seenVers.insert(*vIt);
	}
    }

  for(package_iterator pIt = universe.packages_begin();
      !pIt.end(); ++pIt)
    {
      for(typename package::version_iterator vIt = (*pIt).versions_begin();
	  !vIt.end(); ++vIt)
	{
	  for(typename version::dep_iterator dIt = (*vIt).deps_begin();
	      !dIt.end(); ++dIt)
	    {
	      if((*dIt).get_source() != *vIt)
		{
		  std::cout << "Error: "
			    << *dIt
			    << " is a forward dep of a different version "
			    << *vIt
			    << std::endl;
		}

	      for(typename dep::solver_iterator sIt = (*dIt).solvers_begin();
		  !sIt.end(); ++sIt)
		{
		  if(seenVers.find(*sIt) == seenVers.end())
		    std::cout << "Error: "
			      << *sIt
			      << " exists as a solver of "
			      << *dIt
			      << " but is not a member of the global version list."
			      << std::endl;
		}

	      seenDeps.insert(*dIt);

	      if((*dIt).get_source() != (*vIt))
		{
		  std::cout << "Error: "
			    << (*dIt)
			    << " belongs to the forward dependency list of "
			    << (*vIt)
			    <<std::endl;
		}
	    }

	  for(typename version::revdep_iterator rdIt = (*vIt).revdeps_begin();
	      !rdIt.end(); ++rdIt)
	    {
	      if(seenVers.find((*rdIt).get_source()) == seenVers.end())
		std::cout << "Error: "
			  << (*rdIt).get_source()
			  << " exists as the source of the revdep "
			  << *rdIt
			  << " linked from version "
			  << *vIt
			  << " but is not a member of the global version list."
			  << std::endl;

	      for(typename dep::solver_iterator sIt = (*rdIt).solvers_begin();
		  !sIt.end(); ++sIt)
		{
		  if(seenVers.find(*sIt) == seenVers.end())
		    std::cout << "Error: "
			      << *sIt
			      << " exists as a solver of the revdep "
			      << *rdIt
			      << " linked from "
			      << *vIt
			      << " but is not a member of the global version list."
			      << std::endl;
		}

	      seenRevDeps.insert(*rdIt);

	      bool found = false;
	      for(typename dep::solver_iterator sIt = (*rdIt).solvers_begin();
		  !found && !sIt.end(); ++sIt)
		{
		  std::string solverpkg = (*sIt).get_package().get_name();
		  std::string solverver = (*sIt).get_name();
		  /* heh heh */
		  solverpkg = solverpkg; solverver = solverver;
		  if((*sIt).get_package() == *pIt)
		    found = true;
		}

	      if(!found)
		std::cout << "Error: spurious revdep link from "
			  << (*vIt)
			  << " to "
			  << (*rdIt)
			  << std::endl;
	    }

	  if((*vIt).get_package() != (*pIt))
	    {
	      std::cout << "Error: "
			<< (*vIt)
			<< " is a member of the version list of a different package "
			<< (*pIt).get_name()
			<< std::endl;
	    }
	}

      if((*pIt).current_version().get_package() != (*pIt))
	{
	  std::cout << "Error: the current version of "
		    << (*pIt).get_name()
		    << " is a version of another package, "
		    << (*pIt).current_version()
		    << std::endl;
	}
    }

  for(dep_iterator dIt = universe.deps_begin();
      !dIt.end(); ++dIt)
    {
      // Test solved_by.
      for(package_iterator pIt = universe.packages_begin();
	  !pIt.end(); ++pIt)
	{
	  for(typename package::version_iterator vIt = (*pIt).versions_begin();
	      !vIt.end(); ++vIt)
	    {
	      bool should_be_solver
		= (*pIt) == (*dIt).get_source().get_package() &&
		  (*vIt) != (*dIt).get_source();

	      for(typename dep::solver_iterator sIt = (*dIt).solvers_begin();
		  !should_be_solver && !sIt.end(); ++sIt)
		{
		  if(*sIt == *vIt)
		    should_be_solver = true;
		}

	      if(should_be_solver)
		{
		  if(!(*dIt).solved_by(*vIt))
		    {
		      std::cout << "Error: "
				<< *vIt
				<< " should solve "
				<< *dIt
				<< " but solved_by returned false."
				<< std::endl;
		    }
		}
	      else
		{
		  if((*dIt).solved_by(*vIt))
		    {
		      std::cout << "Error: "
				<< *vIt
				<< " should NOT solve "
				<< *dIt
				<< " but solved_by returned true."
				<< std::endl;
		    }
		}
	    }
	}
    }

  // Check dependency linkage.
  for(dep_iterator dIt = universe.deps_begin();
      !dIt.end(); ++dIt)
    {
      if(seenDeps.find(*dIt) == seenDeps.end())
	std::cout << "Error: "
		  << *dIt
		  << " is not forward-linked from any package."
		  << std::endl;

      if(!(*dIt).solvers_begin().end() &&
	 seenRevDeps.find(*dIt) == seenRevDeps.end())
	std::cout << "Error: "
		  << *dIt
		  << " is not back-linked from any package."
		  << std::endl;

    }

  for(dep_iterator dIt = universe.deps_begin();
      !dIt.end(); ++dIt)
    {
      seenDeps.erase(*dIt);
      seenRevDeps.erase(*dIt);

      {
	// Look for the dependency in it's source's forward list.
	bool found = false;
	for(typename version::dep_iterator dIt2 = (*dIt).get_source().deps_begin();
	    !found && !dIt2.end(); ++dIt2)
	  {
	    if(*dIt2 == *dIt)
	      found = true;
	  }

	if(!found)
	  {
	    std::cout << "Error: Dependency "
		      << (*dIt)
		      << " does not occur in its source's forward list."
		      << std::endl;
	  }
      }

      // Check that this dependency occurs in each of its solver's
      // revdep lists, or in the revdep list of all non-solving
      // versions of that package.
      for(typename dep::solver_iterator sIt = (*dIt).solvers_begin();
	  !sIt.end(); ++sIt)
	{
	  bool found = false;
	  for(typename version::revdep_iterator rdIt = (*sIt).revdeps_begin();
	      !found && !rdIt.end(); ++rdIt)
	    {
	      if(*rdIt == *dIt)
		found = true;
	    }

	  if(!found)
	    {
	      // Check that for each non-solving version of the package,
	      // a revdep link exists.
	      for(typename package::version_iterator vIt = (*sIt).get_package().versions_begin();
		  !found && !vIt.end(); ++vIt)
		{
		  bool is_solver = false;
		  for(typename dep::solver_iterator sIt2 = (*dIt).solvers_begin();
		      !is_solver && !sIt2.end(); ++sIt2)
		    {
		      if(*sIt2 == *vIt)
			is_solver = true;
		    }

		  // This is a non-solving version, so a link must exist.
		  if(!is_solver)
		    {
		      for(typename version::revdep_iterator rdIt = (*vIt).revdeps_begin();
			  !found && !rdIt.end(); ++rdIt)
			{
			  if(*rdIt == *dIt)
			    found = true;
			}

		      if(!found)
			{
			  // TODO: check how many alternates *are* linked
			  // (do all of them fail or just some?)
			  std::cout << "Error: "
				    << *dIt
				    << " is not linked into the revdep list of "
				    << *sIt
				    << " and neither is the alternate version "
				    << *vIt
				    << std::endl;
			}
		    }
		}
	    }
	}
    }

  // TODO: check the reverse containment property too.
  for(typename std::set<dep>::const_iterator it = seenDeps.begin();
      it != seenDeps.end(); ++it)
    {
      std::cout << "Error: "
		<< (*it)
		<< " is contained in a package dep list but not the global dep list."
		<< std::endl;
    }

  for(typename std::set<dep>::const_iterator it = seenRevDeps.begin();
      it != seenRevDeps.end(); ++it)
    {
      std::cout << "Error: "
		<< (*it)
		<< " is contained in a package reverse dep list but not the global dep list."
		<< std::endl;
    }

  resolver_initial_state<PackageUniverse>
    initial_state(imm::map<package, version>(), universe.get_package_count());

  for(broken_dep_iterator bdIt = universe.broken_begin();
      !bdIt.end(); ++bdIt)
    {
      if(!(*bdIt).broken_under(initial_state))
	{
	  std::cout << "Error: "
		    << (*bdIt)
		    <<" is in the broken dep list but isn't broken in the empty solution."
		    << std::endl;
	}
    }
}