summaryrefslogtreecommitdiffstats
path: root/testing/generate_gmock_mutant.py
blob: b881cd99c95cd5cf30496262af73bbf17df50e59 (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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import string
import sys

HEADER = """\
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file automatically generated by testing/generate_gmock_mutant.py.
// DO NOT EDIT.

#ifndef TESTING_GMOCK_MUTANT_H_
#define TESTING_GMOCK_MUTANT_H_

// The intention of this file is to make possible using GMock actions in
// all of its syntactic beauty. Classes and helper functions can be used as
// more generic variants of Task and Callback classes (see base/task.h)
// Mutant supports both pre-bound arguments (like Task) and call-time
// arguments (like Callback) - hence the name. :-)
//
// DispatchToMethod/Function supports two sets of arguments: pre-bound (P) and
// call-time (C). The arguments as well as the return type are templatized.
// DispatchToMethod/Function will also try to call the selected method or
// function even if provided pre-bound arguments does not match exactly with
// the function signature hence the X1, X2 ... XN parameters in CreateFunctor.
// DispatchToMethod will try to invoke method that may not belong to the
// object's class itself but to the object's class base class.
//
// Additionally you can bind the object at calltime by binding a pointer to
// pointer to the object at creation time - before including this file you
// have to #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING.
//
// TODO(stoyan): It's yet not clear to me should we use T& and T&* instead
// of T* and T** when we invoke CreateFunctor to match the EXPECT_CALL style.
//
//
// Sample usage with gMock:
//
// struct Mock : public ObjectDelegate {
//   MOCK_METHOD2(string, OnRequest(int n, const string& request));
//   MOCK_METHOD1(void, OnQuit(int exit_code));
//   MOCK_METHOD2(void, LogMessage(int level, const string& message));
//
//   string HandleFlowers(const string& reply, int n, const string& request) {
//     string result = SStringPrintf("In request of %d %s ", n, request);
//     for (int i = 0; i < n; ++i) result.append(reply)
//     return result;
//   }
//
//   void DoLogMessage(int level, const string& message) {
//   }
//
//   void QuitMessageLoop(int seconds) {
//     base::MessageLoop* loop = base::MessageLoop::current();
//     loop->PostDelayedTask(FROM_HERE,
//                           base::MessageLoop::QuitWhenIdleClosure(),
//                           1000 * seconds);
//   }
// };
//
// Mock mock;
// // Will invoke mock.HandleFlowers("orchids", n, request)
// // "orchids" is a pre-bound argument, and <n> and <request> are call-time
// // arguments - they are not known until the OnRequest mock is invoked.
// EXPECT_CALL(mock, OnRequest(Ge(5), base::StartsWith("flower"))
//   .Times(1)
//   .WillOnce(Invoke(CreateFunctor(&mock, &Mock::HandleFlowers,
//       string("orchids"))));
//
//
// // No pre-bound arguments, two call-time arguments passed
// // directly to DoLogMessage
// EXPECT_CALL(mock, OnLogMessage(_, _))
//   .Times(AnyNumber())
//   .WillAlways(Invoke(CreateFunctor, &mock, &Mock::DoLogMessage));
//
//
// // In this case we have a single pre-bound argument - 3. We ignore
// // all of the arguments of OnQuit.
// EXCEPT_CALL(mock, OnQuit(_))
//   .Times(1)
//   .WillOnce(InvokeWithoutArgs(CreateFunctor(
//       &mock, &Mock::QuitMessageLoop, 3)));
//
// MessageLoop loop;
// loop.Run();
//
//
//  // Here is another example of how we can set an action that invokes
//  // method of an object that is not yet created.
// struct Mock : public ObjectDelegate {
//   MOCK_METHOD1(void, DemiurgeCreated(Demiurge*));
//   MOCK_METHOD2(void, OnRequest(int count, const string&));
//
//   void StoreDemiurge(Demiurge* w) {
//     demiurge_ = w;
//   }
//
//   Demiurge* demiurge;
// }
//
// EXPECT_CALL(mock, DemiurgeCreated(_)).Times(1)
//    .WillOnce(Invoke(CreateFunctor(&mock, &Mock::StoreDemiurge)));
//
// EXPECT_CALL(mock, OnRequest(_, StrEq("Moby Dick")))
//    .Times(AnyNumber())
//    .WillAlways(WithArgs<0>(Invoke(
//        CreateFunctor(&mock->demiurge_, &Demiurge::DecreaseMonsters))));
//

#include "base/memory/linked_ptr.h"
#include "base/tuple.h"

namespace testing {"""

MUTANT = """\

// Interface that is exposed to the consumer, that does the actual calling
// of the method.
template <typename R, typename Params>
class MutantRunner {
 public:
  virtual R RunWithParams(const Params& params) = 0;
  virtual ~MutantRunner() {}
};

// Mutant holds pre-bound arguments (like Task). Like Callback
// allows call-time arguments. You bind a pointer to the object
// at creation time.
template <typename R, typename T, typename Method,
          typename PreBound, typename Params>
class Mutant : public MutantRunner<R, Params> {
 public:
  Mutant(T* obj, Method method, const PreBound& pb)
      : obj_(obj), method_(method), pb_(pb) {
  }

  // MutantRunner implementation
  virtual R RunWithParams(const Params& params) {
    return DispatchToMethod<R>(this->obj_, this->method_, pb_, params);
  }

  T* obj_;
  Method method_;
  PreBound pb_;
};

template <typename R, typename Function, typename PreBound, typename Params>
class MutantFunction : public MutantRunner<R, Params> {
 public:
  MutantFunction(Function function, const PreBound& pb)
      : function_(function), pb_(pb) {
  }

  // MutantRunner implementation
  virtual R RunWithParams(const Params& params) {
    return DispatchToFunction<R>(function_, pb_, params);
  }

  Function function_;
  PreBound pb_;
};

#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
// MutantLateBind is like Mutant, but you bind a pointer to a pointer
// to the object. This way you can create actions for an object
// that is not yet created (has only storage for a pointer to it).
template <typename R, typename T, typename Method,
          typename PreBound, typename Params>
class MutantLateObjectBind : public MutantRunner<R, Params> {
 public:
  MutantLateObjectBind(T** obj, Method method, const PreBound& pb)
      : obj_(obj), method_(method), pb_(pb) {
  }

  // MutantRunner implementation.
  virtual R RunWithParams(const Params& params) {
    EXPECT_THAT(*this->obj_, testing::NotNull());
    if (NULL == *this->obj_)
      return R();
    return DispatchToMethod<R>( *this->obj_, this->method_, pb_, params);
  }

  T** obj_;
  Method method_;
  PreBound pb_;
};
#endif

// Simple MutantRunner<> wrapper acting as a functor.
// Redirects operator() to MutantRunner<Params>::Run()
template <typename R, typename Params>
struct MutantFunctor {
  explicit MutantFunctor(MutantRunner<R, Params>*  cb) : impl_(cb) {
  }

  ~MutantFunctor() {
  }

  inline R operator()() {
    return impl_->RunWithParams(base::Tuple<>());
  }

  template <typename Arg1>
  inline R operator()(const Arg1& a) {
    return impl_->RunWithParams(Params(a));
  }

  template <typename Arg1, typename Arg2>
  inline R operator()(const Arg1& a, const Arg2& b) {
    return impl_->RunWithParams(Params(a, b));
  }

  template <typename Arg1, typename Arg2, typename Arg3>
  inline R operator()(const Arg1& a, const Arg2& b, const Arg3& c) {
    return impl_->RunWithParams(Params(a, b, c));
  }

  template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
  inline R operator()(const Arg1& a, const Arg2& b, const Arg3& c,
                         const Arg4& d) {
    return impl_->RunWithParams(Params(a, b, c, d));
  }

 private:
  // We need copy constructor since MutantFunctor is copied few times
  // inside GMock machinery, hence no DISALLOW_EVIL_CONTRUCTORS
  MutantFunctor();
  linked_ptr<MutantRunner<R, Params> > impl_;
};
"""

FOOTER = """\
}  // namespace testing

#endif  // TESTING_GMOCK_MUTANT_H_"""

# Templates for DispatchToMethod/DispatchToFunction functions.
# template_params - typename P1, typename P2.. typename C1..
# prebound - Tuple<P1, .. PN>
# calltime - Tuple<C1, .. CN>
# args - p.a, p.b.., c.a, c.b..
DISPATCH_TO_METHOD_TEMPLATE = """\
template <typename R, typename T, typename Method, %(template_params)s>
inline R DispatchToMethod(T* obj, Method method,
                          const %(prebound)s& p,
                          const %(calltime)s& c) {
  return (obj->*method)(%(args)s);
}
"""

DISPATCH_TO_FUNCTION_TEMPLATE = """\
template <typename R, typename Function, %(template_params)s>
inline R DispatchToFunction(Function function,
                            const %(prebound)s& p,
                            const %(calltime)s& c) {
  return (*function)(%(args)s);
}
"""

# Templates for CreateFunctor functions.
# template_params - typename P1, typename P2.. typename C1.. typename X1..
# prebound - Tuple<P1, .. PN>
# calltime - Tuple<A1, .. AN>
# params - X1,.. , A1, ..
# args - const P1& p1 ..
# call_args - p1, p2, p3..
CREATE_METHOD_FUNCTOR_TEMPLATE = """\
template <typename R, typename T, typename U, %(template_params)s>
inline MutantFunctor<R, %(calltime)s>
CreateFunctor(T* obj, R (U::*method)(%(params)s), %(args)s) {
  MutantRunner<R, %(calltime)s>* t =
      new Mutant<R, T, R (U::*)(%(params)s),
                 %(prebound)s, %(calltime)s>
          (obj, method, base::MakeTuple(%(call_args)s));
  return MutantFunctor<R, %(calltime)s>(t);
}
"""

CREATE_FUNCTION_FUNCTOR_TEMPLATE = """\
template <typename R, %(template_params)s>
inline MutantFunctor<R, %(calltime)s>
CreateFunctor(R (*function)(%(params)s), %(args)s) {
  MutantRunner<R, %(calltime)s>* t =
      new MutantFunction<R, R (*)(%(params)s),
                         %(prebound)s, %(calltime)s>
          (function, base::MakeTuple(%(call_args)s));
  return MutantFunctor<R, %(calltime)s>(t);
}
"""

def SplitLine(line, width):
  """Splits a single line at comma, at most |width| characters long."""
  if len(line) <= width:
    return (line, None)
  n = 1 + line[:width].rfind(",")
  if n == 0:  # If comma cannot be found give up and return the entire line.
    return (line, None)
  # Assume there is a space after the comma
  assert line[n] == " "
  return (line[:n], line[n + 1:])


def Wrap(s, width, subsequent_offset):
  """Wraps a single line |s| at commas so every line is at most |width|
     characters long.
  """
  w = []
  spaces = " " * subsequent_offset
  while s:
    (f, s) = SplitLine(s, width)
    w.append(f)
    if s:
      s = spaces  + s
  return "\n".join(w)


def Clean(s):
  """Cleans artifacts from generated C++ code.

  Our simple string formatting/concatenation may introduce extra commas.
  """
  s = s.replace(", >", ">")
  s = s.replace(", )", ")")
  return s


def ExpandPattern(pattern, it):
  """Return list of expanded pattern strings.

  Each string is created by replacing all '%' in |pattern| with element of |it|.
  """
  return [pattern.replace("%", x) for x in it]


def Gen(pattern, n, start):
  """Expands pattern replacing '%' with sequential integers starting with start.

  Expanded patterns will be joined with comma separator.
  Gen("X%", 3, 1) will return "X1, X2, X3".
  """
  it = string.hexdigits[start:n + start]
  return ", ".join(ExpandPattern(pattern, it))


def Merge(a):
  return ", ".join(filter(len, a))


def GenTuple(pattern, n):
  return Clean("base::Tuple<%s>" % (Gen(pattern, n, 1)))


def FixCode(s):
  lines = Clean(s).splitlines()
  # Wrap sometimes very long 1st line to be inside the "template <"
  lines[0] = Wrap(lines[0], 80, 10)

  # Wrap all subsequent lines to 6 spaces arbitrarily. This is a 2-space line
  # indent, plus a 4 space continuation indent.
  for line in xrange(1, len(lines)):
    lines[line] = Wrap(lines[line], 80, 6)
  return "\n".join(lines)


def GenerateDispatch(prebound, calltime):
  print "\n// %d - %d" % (prebound, calltime)
  args = {
      "template_params": Merge([Gen("typename P%", prebound, 1),
                                Gen("typename C%", calltime, 1)]),
      "prebound": GenTuple("P%", prebound),
      "calltime": GenTuple("C%", calltime),
      "args": Merge([Gen("base::get<%>(p)", prebound, 0),
                     Gen("base::get<%>(c)", calltime, 0)]),
  }

  print FixCode(DISPATCH_TO_METHOD_TEMPLATE % args)
  print FixCode(DISPATCH_TO_FUNCTION_TEMPLATE % args)


def GenerateCreateFunctor(prebound, calltime):
  print "// %d - %d" % (prebound, calltime)
  args = {
      "calltime": GenTuple("A%", calltime),
      "prebound": GenTuple("P%", prebound),
      "params": Merge([Gen("X%", prebound, 1), Gen("A%", calltime, 1)]),
      "args": Gen("const P%& p%", prebound, 1),
      "call_args": Gen("p%", prebound, 1),
      "template_params": Merge([Gen("typename P%", prebound, 1),
                                Gen("typename A%", calltime, 1),
                                Gen("typename X%", prebound, 1)])
  }

  mutant = FixCode(CREATE_METHOD_FUNCTOR_TEMPLATE % args)
  print mutant

  # Slightly different version for free function call.
  print "\n", FixCode(CREATE_FUNCTION_FUNCTOR_TEMPLATE % args)

  # Functor with pointer to a pointer of the object.
  print "\n#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING"
  mutant2 = mutant.replace("CreateFunctor(T* obj,", "CreateFunctor(T** obj,")
  mutant2 = mutant2.replace("new Mutant", "new MutantLateObjectBind")
  mutant2 = mutant2.replace(" " * 17 + "Tuple", " " * 31 + "Tuple")
  print mutant2
  print "#endif  // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING\n"

  # OS_WIN specific. Same functors but with stdcall calling conventions.
  # These are not for WIN64 (x86_64) because there is only one calling
  # convention in WIN64.
  # Functor for method with __stdcall calling conventions.
  print "#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64)"
  stdcall_method = CREATE_METHOD_FUNCTOR_TEMPLATE
  stdcall_method = stdcall_method.replace("U::", "__stdcall U::")
  stdcall_method = FixCode(stdcall_method % args)
  print stdcall_method
  # Functor for free function with __stdcall calling conventions.
  stdcall_function = CREATE_FUNCTION_FUNCTOR_TEMPLATE
  stdcall_function = stdcall_function.replace("R (*", "R (__stdcall *")
  print "\n", FixCode(stdcall_function % args)

  print "#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING"
  stdcall2 = stdcall_method
  stdcall2 = stdcall2.replace("CreateFunctor(T* obj,", "CreateFunctor(T** obj,")
  stdcall2 = stdcall2.replace("new Mutant", "new MutantLateObjectBind")
  stdcall2 = stdcall2.replace(" " * 17 + "Tuple", " " * 31 + "Tuple")
  print stdcall2
  print "#endif  // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING"
  print "#endif  // defined (OS_WIN) && !defined (ARCH_CPU_X86_64)\n"


def main():
  print HEADER
  for prebound in xrange(0, 6 + 1):
    for args in xrange(0, 6 + 1):
      GenerateDispatch(prebound, args)
  print MUTANT
  for prebound in xrange(0, 6 + 1):
    for args in xrange(0, 6 + 1):
      GenerateCreateFunctor(prebound, args)
  print FOOTER
  return 0


if __name__ == "__main__":
  sys.exit(main())