Counter expressions (#3534)
* Add peglib * - Add expression engine - Take an expression when setting a counter * Shift + Click = Middleclick for counters * minor cleanup for clangify Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com> * Added tip entry
This commit is contained in:
parent
6e1b0a7590
commit
c9c0fb28ee
13 changed files with 3508 additions and 26 deletions
|
@ -14,6 +14,7 @@ include=("common" \
|
||||||
"servatrice/src")
|
"servatrice/src")
|
||||||
exclude=("servatrice/src/smtp" \
|
exclude=("servatrice/src/smtp" \
|
||||||
"common/sfmt" \
|
"common/sfmt" \
|
||||||
|
"common/lib" \
|
||||||
"oracle/src/zip" \
|
"oracle/src/zip" \
|
||||||
"oracle/src/lzma" \
|
"oracle/src/lzma" \
|
||||||
"oracle/src/qt-json")
|
"oracle/src/qt-json")
|
||||||
|
|
|
@ -351,6 +351,7 @@
|
||||||
<file>resources/tips/images/cockatrice_register.png</file>
|
<file>resources/tips/images/cockatrice_register.png</file>
|
||||||
<file>resources/tips/images/cockatrice_wiki.png</file>
|
<file>resources/tips/images/cockatrice_wiki.png</file>
|
||||||
<file>resources/tips/images/coin_flip.png</file>
|
<file>resources/tips/images/coin_flip.png</file>
|
||||||
|
<file>resources/tips/images/counter_expression.png</file>
|
||||||
<file>resources/tips/images/face_down.png</file>
|
<file>resources/tips/images/face_down.png</file>
|
||||||
<file>resources/tips/images/filter_games.png</file>
|
<file>resources/tips/images/filter_games.png</file>
|
||||||
<file>resources/tips/images/github_logo.png</file>
|
<file>resources/tips/images/github_logo.png</file>
|
||||||
|
|
BIN
cockatrice/resources/tips/images/counter_expression.png
Normal file
BIN
cockatrice/resources/tips/images/counter_expression.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
|
@ -83,4 +83,10 @@
|
||||||
<image>face_down.png</image>
|
<image>face_down.png</image>
|
||||||
<date>2018-03-01</date>
|
<date>2018-03-01</date>
|
||||||
</tip>
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Counter expressions</title>
|
||||||
|
<text>When setting a counter value, you can type a math expression in the box and the counter will be set to the result.<br>The "x" variable contains the current counter value.</text>
|
||||||
|
<image>counter_expression.png</image>
|
||||||
|
<date>2019-02-02</date>
|
||||||
|
</tip>
|
||||||
</tips>
|
</tips>
|
|
@ -1,9 +1,11 @@
|
||||||
#include "abstractcounter.h"
|
#include "abstractcounter.h"
|
||||||
|
#include "expression.h"
|
||||||
#include "pb/command_inc_counter.pb.h"
|
#include "pb/command_inc_counter.pb.h"
|
||||||
#include "pb/command_set_counter.pb.h"
|
#include "pb/command_set_counter.pb.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "settingscache.h"
|
#include "settingscache.h"
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
#include <QGraphicsSceneHoverEvent>
|
#include <QGraphicsSceneHoverEvent>
|
||||||
#include <QGraphicsSceneMouseEvent>
|
#include <QGraphicsSceneMouseEvent>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
@ -17,7 +19,7 @@ AbstractCounter::AbstractCounter(Player *_player,
|
||||||
bool _useNameForShortcut,
|
bool _useNameForShortcut,
|
||||||
QGraphicsItem *parent)
|
QGraphicsItem *parent)
|
||||||
: QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value),
|
: QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value),
|
||||||
useNameForShortcut(_useNameForShortcut), hovered(false), aDec(0), aInc(0), dialogSemaphore(false),
|
useNameForShortcut(_useNameForShortcut), hovered(false), aDec(nullptr), aInc(nullptr), dialogSemaphore(false),
|
||||||
deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea)
|
deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea)
|
||||||
{
|
{
|
||||||
setAcceptHoverEvents(true);
|
setAcceptHoverEvents(true);
|
||||||
|
@ -114,7 +116,11 @@ void AbstractCounter::setValue(int _value)
|
||||||
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (isUnderMouse() && player->getLocal()) {
|
if (isUnderMouse() && player->getLocal()) {
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::MidButton || (QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
|
||||||
|
if (menu)
|
||||||
|
menu->exec(event->screenPos());
|
||||||
|
event->accept();
|
||||||
|
} else if (event->button() == Qt::LeftButton) {
|
||||||
Command_IncCounter cmd;
|
Command_IncCounter cmd;
|
||||||
cmd.set_counter_id(id);
|
cmd.set_counter_id(id);
|
||||||
cmd.set_delta(1);
|
cmd.set_delta(1);
|
||||||
|
@ -126,10 +132,6 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||||
cmd.set_delta(-1);
|
cmd.set_delta(-1);
|
||||||
player->sendGameCommand(cmd);
|
player->sendGameCommand(cmd);
|
||||||
event->accept();
|
event->accept();
|
||||||
} else if (event->button() == Qt::MidButton) {
|
|
||||||
if (menu)
|
|
||||||
menu->exec(event->screenPos());
|
|
||||||
event->accept();
|
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
event->ignore();
|
event->ignore();
|
||||||
|
@ -160,8 +162,12 @@ void AbstractCounter::setCounter()
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
dialogSemaphore = true;
|
dialogSemaphore = true;
|
||||||
int newValue = QInputDialog::getInt(0, tr("Set counter"), tr("New value for counter '%1':").arg(name), value,
|
QString expression = QInputDialog::getText(nullptr, tr("Set counter"), tr("New value for counter '%1':").arg(name),
|
||||||
-2000000000, 2000000000, 1, &ok);
|
QLineEdit::Normal, QString::number(value), &ok);
|
||||||
|
|
||||||
|
Expression exp(value);
|
||||||
|
int newValue = static_cast<int>(exp.parse(expression));
|
||||||
|
|
||||||
if (deleteAfterDialog) {
|
if (deleteAfterDialog) {
|
||||||
deleteLater();
|
deleteLater();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -11,6 +11,7 @@ class AbstractCounter : public QObject, public QGraphicsItem
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_INTERFACES(QGraphicsItem)
|
Q_INTERFACES(QGraphicsItem)
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Player *player;
|
Player *player;
|
||||||
int id;
|
int id;
|
||||||
|
@ -18,15 +19,17 @@ protected:
|
||||||
int value;
|
int value;
|
||||||
bool useNameForShortcut, hovered;
|
bool useNameForShortcut, hovered;
|
||||||
|
|
||||||
void mousePressEvent(QGraphicsSceneMouseEvent *event);
|
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
|
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
||||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
|
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QAction *aSet, *aDec, *aInc;
|
QAction *aSet, *aDec, *aInc;
|
||||||
QMenu *menu;
|
QMenu *menu;
|
||||||
bool dialogSemaphore, deleteAfterDialog;
|
bool dialogSemaphore, deleteAfterDialog;
|
||||||
bool shownInCounterArea;
|
bool shownInCounterArea;
|
||||||
|
bool shortcutActive;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void refreshShortcuts();
|
void refreshShortcuts();
|
||||||
void incrementCounter();
|
void incrementCounter();
|
||||||
|
@ -39,14 +42,19 @@ public:
|
||||||
bool _shownInCounterArea,
|
bool _shownInCounterArea,
|
||||||
int _value,
|
int _value,
|
||||||
bool _useNameForShortcut = false,
|
bool _useNameForShortcut = false,
|
||||||
QGraphicsItem *parent = 0);
|
QGraphicsItem *parent = nullptr);
|
||||||
~AbstractCounter();
|
~AbstractCounter() override;
|
||||||
|
|
||||||
|
void retranslateUi();
|
||||||
|
void setValue(int _value);
|
||||||
|
void setShortcutsActive();
|
||||||
|
void setShortcutsInactive();
|
||||||
|
void delCounter();
|
||||||
|
|
||||||
QMenu *getMenu() const
|
QMenu *getMenu() const
|
||||||
{
|
{
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
void retranslateUi();
|
|
||||||
|
|
||||||
int getId() const
|
int getId() const
|
||||||
{
|
{
|
||||||
|
@ -64,12 +72,6 @@ public:
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
void setValue(int _value);
|
|
||||||
void delCounter();
|
|
||||||
|
|
||||||
void setShortcutsActive();
|
|
||||||
void setShortcutsInactive();
|
|
||||||
bool shortcutActive;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#define MIN_TIP_IMAGE_HEIGHT 200
|
#define MIN_TIP_IMAGE_HEIGHT 200
|
||||||
#define MIN_TIP_IMAGE_WIDTH 200
|
#define MIN_TIP_IMAGE_WIDTH 200
|
||||||
#define MAX_TIP_IMAGE_HEIGHT 300
|
#define MAX_TIP_IMAGE_HEIGHT 300
|
||||||
#define MAX_TIP_IMAGE_WIDTH 300
|
#define MAX_TIP_IMAGE_WIDTH 500
|
||||||
|
|
||||||
DlgTipOfTheDay::DlgTipOfTheDay(QWidget *parent) : QDialog(parent)
|
DlgTipOfTheDay::DlgTipOfTheDay(QWidget *parent) : QDialog(parent)
|
||||||
{
|
{
|
||||||
|
@ -149,9 +149,9 @@ void DlgTipOfTheDay::updateTip(int tipId)
|
||||||
qDebug() << "Image failed to load from" << imagePath;
|
qDebug() << "Image failed to load from" << imagePath;
|
||||||
imageLabel->clear();
|
imageLabel->clear();
|
||||||
} else {
|
} else {
|
||||||
int h = std::min(std::max(image->height(), MIN_TIP_IMAGE_HEIGHT), MAX_TIP_IMAGE_HEIGHT);
|
int h = std::min(std::max(imageLabel->height(), MIN_TIP_IMAGE_HEIGHT), MAX_TIP_IMAGE_HEIGHT);
|
||||||
int w = std::min(std::max(imageLabel->width(), MIN_TIP_IMAGE_WIDTH), MAX_TIP_IMAGE_WIDTH);
|
int w = std::min(std::max(imageLabel->width(), MIN_TIP_IMAGE_WIDTH), MAX_TIP_IMAGE_WIDTH);
|
||||||
imageLabel->setPixmap(image->scaled(h, w, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
imageLabel->setPixmap(image->scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
}
|
}
|
||||||
|
|
||||||
date->setText("<i>Tip added on: " + tip.getDate().toString("yyyy.MM.dd") + "</i>");
|
date->setText("<i>Tip added on: " + tip.getDate().toString("yyyy.MM.dd") + "</i>");
|
||||||
|
@ -163,9 +163,7 @@ void DlgTipOfTheDay::updateTip(int tipId)
|
||||||
|
|
||||||
void DlgTipOfTheDay::resizeEvent(QResizeEvent *event)
|
void DlgTipOfTheDay::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
int h = imageLabel->height();
|
imageLabel->setPixmap(image->scaled(imageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
int w = imageLabel->width();
|
|
||||||
imageLabel->setPixmap(image->scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
|
||||||
|
|
||||||
QWidget::resizeEvent(event);
|
QWidget::resizeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ SET(common_SOURCES
|
||||||
server_room.cpp
|
server_room.cpp
|
||||||
serverinfo_user_container.cpp
|
serverinfo_user_container.cpp
|
||||||
sfmt/SFMT.c
|
sfmt/SFMT.c
|
||||||
|
expression.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(ORACLE_LIBS)
|
set(ORACLE_LIBS)
|
||||||
|
|
108
common/expression.cpp
Normal file
108
common/expression.cpp
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
#include "expression.h"
|
||||||
|
#include "./lib/peglib.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
peg::parser math(R"(
|
||||||
|
EXPRESSION <- P0
|
||||||
|
P0 <- P1 (P1_OPERATOR P1)*
|
||||||
|
P1 <- P2 (P2_OPERATOR P2)*
|
||||||
|
P2 <- P3 (P3_OPERATOR P3)*
|
||||||
|
P3 <- NUMBER / FUNCTION / VARIABLE / '(' P0 ')'
|
||||||
|
|
||||||
|
P1_OPERATOR <- < [-+] >
|
||||||
|
P2_OPERATOR <- < [/*] >
|
||||||
|
P3_OPERATOR <- < '^' >
|
||||||
|
|
||||||
|
NUMBER <- < '-'? [0-9]+ >
|
||||||
|
NAME <- < [a-z][a-z0-9]* >
|
||||||
|
VARIABLE <- < [x] >
|
||||||
|
FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')'
|
||||||
|
|
||||||
|
%whitespace <- [ \t\r]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
QMap<QString, std::function<double(double)>> *default_functions = nullptr;
|
||||||
|
|
||||||
|
Expression::Expression(double initial) : value(initial)
|
||||||
|
{
|
||||||
|
if (default_functions == nullptr) {
|
||||||
|
default_functions = new QMap<QString, std::function<double(double)>>();
|
||||||
|
default_functions->insert("sin", [](double a) { return sin(a); });
|
||||||
|
default_functions->insert("cos", [](double a) { return cos(a); });
|
||||||
|
default_functions->insert("tan", [](double a) { return tan(a); });
|
||||||
|
default_functions->insert("sqrt", [](double a) { return sqrt(a); });
|
||||||
|
default_functions->insert("log", [](double a) { return log(a); });
|
||||||
|
default_functions->insert("log10", [](double a) { return log(a); });
|
||||||
|
default_functions->insert("trunc", [](double a) { return trunc(a); });
|
||||||
|
default_functions->insert("abs", [](double a) { return abs(a); });
|
||||||
|
|
||||||
|
default_functions->insert("floor", [](double a) { return floor(a); });
|
||||||
|
default_functions->insert("ceil", [](double a) { return ceil(a); });
|
||||||
|
default_functions->insert("round", [](double a) { return round(a); });
|
||||||
|
default_functions->insert("trunc", [](double a) { return trunc(a); });
|
||||||
|
}
|
||||||
|
fns = QMap<QString, std::function<double(double)>>(*default_functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
double Expression::eval(const peg::Ast &ast)
|
||||||
|
{
|
||||||
|
const auto &nodes = ast.nodes;
|
||||||
|
if (ast.name == "NUMBER") {
|
||||||
|
return stod(ast.token);
|
||||||
|
} else if (ast.name == "FUNCTION") {
|
||||||
|
QString name = QString::fromStdString(nodes[0]->token);
|
||||||
|
if (!fns.contains(name))
|
||||||
|
return 0;
|
||||||
|
return fns[name](eval(*nodes[1]));
|
||||||
|
} else if (ast.name == "VARIABLE") {
|
||||||
|
return value;
|
||||||
|
} else if (ast.name[0] == 'P') {
|
||||||
|
double result = eval(*nodes[0]);
|
||||||
|
for (int i = 1; i < nodes.size(); i += 2) {
|
||||||
|
double arg = eval(*nodes[i + 1]);
|
||||||
|
char operation = nodes[i]->token[0];
|
||||||
|
switch (operation) {
|
||||||
|
case '+':
|
||||||
|
result += arg;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
result -= arg;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
result *= arg;
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
result /= arg;
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
result = pow(result, arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double Expression::parse(const QString &expr)
|
||||||
|
{
|
||||||
|
QByteArray ba = expr.toLocal8Bit();
|
||||||
|
|
||||||
|
math.enable_ast();
|
||||||
|
|
||||||
|
std::shared_ptr<peg::Ast> ast;
|
||||||
|
if (math.parse(ba.data(), ast)) {
|
||||||
|
ast = peg::AstOptimizer(true).optimize(ast);
|
||||||
|
return eval(*ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
28
common/expression.h
Normal file
28
common/expression.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef EXPRESSION_H
|
||||||
|
#define EXPRESSION_H
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QString>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace peg
|
||||||
|
{
|
||||||
|
template <typename Annotation> struct AstBase;
|
||||||
|
struct EmptyType;
|
||||||
|
typedef AstBase<EmptyType> Ast;
|
||||||
|
} // namespace peg
|
||||||
|
|
||||||
|
class Expression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double value;
|
||||||
|
|
||||||
|
explicit Expression(double initial = 0);
|
||||||
|
double parse(const QString &expr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
double eval(const peg::Ast &ast);
|
||||||
|
QMap<QString, std::function<double(double)>> fns;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
3293
common/lib/peglib.h
Normal file
3293
common/lib/peglib.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,11 @@
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_test(NAME dummy_test COMMAND dummy_test)
|
add_test(NAME dummy_test COMMAND dummy_test)
|
||||||
|
add_test(NAME expression_test COMMAND expression_test)
|
||||||
|
|
||||||
# Find GTest
|
# Find GTest
|
||||||
|
|
||||||
add_executable(dummy_test dummy_test.cpp)
|
add_executable(dummy_test dummy_test.cpp)
|
||||||
|
add_executable(expression_test expression_test.cpp)
|
||||||
|
|
||||||
find_package(GTest)
|
find_package(GTest)
|
||||||
|
|
||||||
|
@ -32,10 +34,16 @@ if(NOT GTEST_FOUND)
|
||||||
SET(GTEST_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/gtest-src/include")
|
SET(GTEST_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/gtest-src/include")
|
||||||
SET(GTEST_BOTH_LIBRARIES gtest)
|
SET(GTEST_BOTH_LIBRARIES gtest)
|
||||||
add_dependencies(dummy_test gtest)
|
add_dependencies(dummy_test gtest)
|
||||||
|
add_dependencies(expression_test gtest)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(Qt5 COMPONENTS Widgets REQUIRED)
|
||||||
|
set(TEST_QT_MODULES Qt5::Widgets)
|
||||||
|
|
||||||
|
|
||||||
include_directories(${GTEST_INCLUDE_DIRS})
|
include_directories(${GTEST_INCLUDE_DIRS})
|
||||||
target_link_libraries(dummy_test ${GTEST_BOTH_LIBRARIES})
|
target_link_libraries(dummy_test ${GTEST_BOTH_LIBRARIES})
|
||||||
|
target_link_libraries(expression_test cockatrice_common ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
|
||||||
|
|
||||||
add_subdirectory(carddatabase)
|
add_subdirectory(carddatabase)
|
||||||
add_subdirectory(loading_from_clipboard)
|
add_subdirectory(loading_from_clipboard)
|
||||||
|
|
30
tests/expression_test.cpp
Normal file
30
tests/expression_test.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "../common/expression.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#define TEST_EXPR(name,a,b) TEST(name, Works) { \
|
||||||
|
Expression exp(8); \
|
||||||
|
ASSERT_EQ(exp.parse(a), b) << a; \
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
TEST_EXPR(Number, "1", 1)
|
||||||
|
TEST_EXPR(Multiply, "2*2", 4)
|
||||||
|
TEST_EXPR(Whitespace, "3 * 3", 9)
|
||||||
|
TEST_EXPR(Powers, "2^8", 256)
|
||||||
|
TEST_EXPR(OrderOfOperations, "2+2*2", 6)
|
||||||
|
TEST_EXPR(Fn, "2*cos(1)", 2*cos(1))
|
||||||
|
TEST_EXPR(Variable, "x / 2", 4)
|
||||||
|
TEST_EXPR(Negative, "-2 * 2", -4)
|
||||||
|
TEST_EXPR(UnknownFnReturnsZero, "blah(22)", 0)
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
Loading…
Reference in a new issue