<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include "Support/CmdLine.h"
#include "Support/PrettyPrint.h"
#include "Support/StringSplit.h"

#include <iostream>
#include <set>

#include <gtest/gtest.h>

using namespace support;

typedef std::vector<std::string> Argv;

bool parse(cl::CmdLine& cmd, Argv argv)
{
    if (!cmd.parse(std::move(argv)))
    {
        for (auto const& s : cmd.getErrors())
            std::cout << "NOTE : " << s << "\n";
        return false;
    }

    return true;
}

TEST(CmdLineTest, ArgOptionalPass1)
{
    cl::CmdLine cmd("program");
    auto a = cl::makeOption<std::string>(cmd, "a", cl::ArgOptional);

    EXPECT_TRUE( cmd.parse({"-a"}) );
    EXPECT_EQ( a.getCount(), 1 );
    EXPECT_EQ( a.get(), "" );
}

TEST(CmdLineTest, ArgOptionalPass2)
{
    cl::CmdLine cmd("program");
    auto a = cl::makeOption<std::string>(cmd, "a", cl::ArgOptional);

    EXPECT_TRUE( cmd.parse({"-a=xxx"}) );
    EXPECT_EQ( a.getCount(), 1 );
    EXPECT_EQ( a.get(), "xxx" );
}

TEST(CmdLineTest, ArgOptionalFail1)
{
    cl::CmdLine cmd("program");
    auto a = cl::makeOption<std::string>(cmd, "a", cl::ArgOptional);

    EXPECT_FALSE( cmd.parse({"-a", "xxx"}) );
    EXPECT_EQ( a.getCount(), 1 );
    EXPECT_EQ( a.get(), "" );

    auto e = cmd.getErrors();

    ASSERT_EQ(e.size(), 1);
    EXPECT_EQ(e[0], "unhandled positional argument: 'xxx'");
}

TEST(CmdLineTest, Flags1)
{
    auto test = [](Argv argv, bool val_a, bool val_b, bool val_c) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<bool>(cmd, "a");
        auto b = cl::makeOption<bool>(cmd, "b", cl::Grouping);
        auto c = cl::makeOption<bool>(cmd, "c", cl::Grouping);

        if (!parse(cmd, std::move(argv)))
            return false;

        if (a.getCount()) EXPECT_EQ(a.get(), val_a);
        if (b.getCount()) EXPECT_EQ(b.get(), val_b);
        if (c.getCount()) EXPECT_EQ(c.get(), val_c);
        return true;
    };

    EXPECT_TRUE ( test({ "-a"                   }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=1"                 }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=true"              }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=0"                 }, 0, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=false"             }, 0, 1, 1 ) );
    EXPECT_FALSE( test({ "-a0"                  }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-a1"                  }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-ax"                  }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a", "-b"             }, 1, 1, 0 ) );
    EXPECT_TRUE ( test({ "-a", "-b", "-c"       }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a", "-bc"            }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a", "-cb"            }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-a", "-bcbc"          }, 1, 1, 1 ) );
}

TEST(CmdLineTest, Grouping1)
{
    auto test = [](Argv argv, int cnt_a, int cnt_b, int cnt_c) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<bool>(cmd, "a", cl::Grouping, cl::ZeroOrMore);
        auto b = cl::makeOption<bool>(cmd, "b", cl::Grouping);
        auto c = cl::makeOption<bool>(cmd, "ab", cl::Prefix);

        if (!parse(cmd, std::move(argv)))
            return false;

        EXPECT_EQ(a.getCount(), cnt_a);
        EXPECT_EQ(b.getCount(), cnt_b);
        EXPECT_EQ(c.getCount(), cnt_c);
        return true;
    };

    EXPECT_TRUE ( test({ "-a"                   }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=1"                 }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=true"              }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=0"                 }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=false"             }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a0"                  }, 0, 0, 0 ) );
    EXPECT_FALSE( test({ "-a1"                  }, 0, 0, 0 ) );
    EXPECT_FALSE( test({ "-ax"                  }, 0, 0, 0 ) );
    EXPECT_TRUE ( test({ "-ab"                  }, 0, 0, 1 ) );
    EXPECT_FALSE( test({ "-abb"                 }, 0, 0, 0 ) );
    EXPECT_TRUE ( test({ "-abtrue"              }, 0, 0, 1 ) );
    EXPECT_TRUE ( test({ "-abfalse"             }, 0, 0, 1 ) );
    EXPECT_TRUE ( test({ "-ba"                  }, 1, 1, 0 ) );
    EXPECT_TRUE ( test({ "-baa"                 }, 2, 1, 0 ) );
    EXPECT_TRUE ( test({ "-ba", "-a"            }, 2, 1, 0 ) );
    EXPECT_TRUE ( test({ "-ab=1", "-ba"         }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-ab", "1", "-ba"      }, 1, 1, 1 ) );
}

TEST(CmdLineTest, PrefixOptional)
{
    auto test = [](Argv argv, std::string const& a_val) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<std::string>(cmd, "a", cl::Prefix, cl::ArgOptional);
        auto b = cl::makeOption<bool>(cmd, "b");

        if (!parse(cmd, std::move(argv)))
            return false;

        if (a.getCount()) EXPECT_EQ(a.get(), a_val);
        return true;
    };

    EXPECT_TRUE ( test({ "-a"                   }, ""       ) );
    EXPECT_TRUE ( test({ "-a", "-b"             }, ""       ) ); // "-b" is a valid option -> is not a valid argument for -a
    EXPECT_TRUE ( test({ "-axxx"                }, "xxx"    ) );
    EXPECT_TRUE ( test({ "-a=xxx"               }, "xxx"    ) );
    EXPECT_FALSE( test({ "-a", "xxx"            }, ""       ) ); // [unhandled positional]
}

TEST(CmdLineTest, PrefixRequired)
{
    auto test = [](Argv argv, std::string const& a_val) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<std::string>(cmd, "a", cl::Prefix, cl::ArgRequired);
        auto b = cl::makeOption<bool>(cmd, "b");

        if (!parse(cmd, std::move(argv)))
            return false;

        if (a.getCount()) EXPECT_EQ(a.get(), a_val);
        return true;
    };

    EXPECT_FALSE( test({ "-a"                   }, ""       ) );
    EXPECT_FALSE( test({ "-a", "-b"             }, ""       ) ); // "-b" is a valid option -> is not a valid argument for -a
    EXPECT_TRUE ( test({ "-axxx"                }, "xxx"    ) );
    EXPECT_TRUE ( test({ "-a=xxx"               }, "=xxx"   ) );
    EXPECT_FALSE( test({ "-a", "xxx"            }, "xxx"    ) ); // [option 'a' expects and argument, unhandled positional]
}
<commit_msg>Add some additional tests<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include "Support/CmdLine.h"
#include "Support/PrettyPrint.h"
#include "Support/StringSplit.h"

#include <iostream>
#include <set>

#include <gtest/gtest.h>

using namespace support;

typedef std::vector<std::string> Argv;

bool parse(cl::CmdLine& cmd, Argv argv)
{
    if (!cmd.parse(std::move(argv)))
    {
        for (auto const& s : cmd.getErrors())
            std::cout << "NOTE : " << s << "\n";
        return false;
    }

    return true;
}

TEST(CmdLineTest, ArgOptionalPass1)
{
    cl::CmdLine cmd("program");
    auto a = cl::makeOption<std::string>(cmd, "a", cl::ArgOptional);

    EXPECT_TRUE( cmd.parse({"-a"}) );
    EXPECT_EQ( a.getCount(), 1 );
    EXPECT_EQ( a.get(), "" );
}

TEST(CmdLineTest, ArgOptionalPass2)
{
    cl::CmdLine cmd("program");
    auto a = cl::makeOption<std::string>(cmd, "a", cl::ArgOptional);

    EXPECT_TRUE( cmd.parse({"-a=xxx"}) );
    EXPECT_EQ( a.getCount(), 1 );
    EXPECT_EQ( a.get(), "xxx" );
}

TEST(CmdLineTest, ArgOptionalFail1)
{
    cl::CmdLine cmd("program");
    auto a = cl::makeOption<std::string>(cmd, "a", cl::ArgOptional);

    EXPECT_FALSE( cmd.parse({"-a", "xxx"}) );
    EXPECT_EQ( a.getCount(), 1 );
    EXPECT_EQ( a.get(), "" );

    auto e = cmd.getErrors();

    ASSERT_EQ(e.size(), 1);
    EXPECT_EQ(e[0], "unhandled positional argument: 'xxx'");
}

TEST(CmdLineTest, Flags1)
{
    auto test = [](Argv argv, bool val_a, bool val_b, bool val_c) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<bool>(cmd, "a");
        auto b = cl::makeOption<bool>(cmd, "b", cl::Grouping);
        auto c = cl::makeOption<bool>(cmd, "c", cl::Grouping);

        if (!parse(cmd, std::move(argv)))
            return false;

        if (a.getCount()) EXPECT_EQ(a.get(), val_a);
        if (b.getCount()) EXPECT_EQ(b.get(), val_b);
        if (c.getCount()) EXPECT_EQ(c.get(), val_c);
        return true;
    };

    EXPECT_TRUE ( test({ "-a"                   }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=1"                 }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=true"              }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=0"                 }, 0, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a=false"             }, 0, 1, 1 ) );
    EXPECT_FALSE( test({ "-a0"                  }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-a1"                  }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-ax"                  }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a", "-b"             }, 1, 1, 0 ) );
    EXPECT_TRUE ( test({ "-a", "-b", "-c"       }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a", "-bc"            }, 1, 1, 1 ) );
    EXPECT_TRUE ( test({ "-a", "-cb"            }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-a", "-bcbc"          }, 1, 1, 1 ) );
}

TEST(CmdLineTest, Grouping1)
{
    auto test = [](Argv argv, int cnt_a, int cnt_b, int cnt_c) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<bool>(cmd, "a", cl::Grouping, cl::ZeroOrMore);
        auto b = cl::makeOption<bool>(cmd, "b", cl::Grouping);
        auto c = cl::makeOption<bool>(cmd, "ab", cl::Prefix);

        if (!parse(cmd, std::move(argv)))
            return false;

        EXPECT_EQ(a.getCount(), cnt_a);
        EXPECT_EQ(b.getCount(), cnt_b);
        EXPECT_EQ(c.getCount(), cnt_c);
        return true;
    };

    EXPECT_TRUE ( test({ "-a"                   }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=1"                 }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=true"              }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=0"                 }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a=false"             }, 1, 0, 0 ) );
    EXPECT_FALSE( test({ "-a0"                  }, 0, 0, 0 ) );
    EXPECT_FALSE( test({ "-a1"                  }, 0, 0, 0 ) );
    EXPECT_FALSE( test({ "-ax"                  }, 0, 0, 0 ) );
    EXPECT_TRUE ( test({ "-ab"                  }, 0, 0, 1 ) );
    EXPECT_FALSE( test({ "-abb"                 }, 0, 0, 0 ) );
    EXPECT_TRUE ( test({ "-abtrue"              }, 0, 0, 1 ) );
    EXPECT_TRUE ( test({ "-abfalse"             }, 0, 0, 1 ) );
    EXPECT_TRUE ( test({ "-ba"                  }, 1, 1, 0 ) );
    EXPECT_TRUE ( test({ "-baa"                 }, 2, 1, 0 ) );
    EXPECT_TRUE ( test({ "-ba", "-a"            }, 2, 1, 0 ) );
    EXPECT_TRUE ( test({ "-ab=1", "-ba"         }, 1, 1, 1 ) );
    EXPECT_FALSE( test({ "-ab", "1", "-ba"      }, 1, 1, 1 ) );
}

TEST(CmdLineTest, PrefixOptional)
{
    auto test = [](Argv argv, std::string const& a_val) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<std::string>(cmd, "a", cl::Prefix, cl::ArgOptional);
        auto b = cl::makeOption<bool>(cmd, "b");

        if (!parse(cmd, std::move(argv)))
            return false;

        if (a.getCount()) EXPECT_EQ(a.get(), a_val);
        return true;
    };

    EXPECT_TRUE ( test({ "-a"                   }, ""       ) );
    EXPECT_TRUE ( test({ "-a", "-b"             }, ""       ) ); // "-b" is a valid option -> is not a valid argument for -a
    EXPECT_TRUE ( test({ "-a-b"                 }, "-b"     ) );
    EXPECT_TRUE ( test({ "-a=-b"                }, "-b"     ) );
    EXPECT_TRUE ( test({ "-axxx"                }, "xxx"    ) );
    EXPECT_TRUE ( test({ "-a=xxx"               }, "xxx"    ) );
    EXPECT_FALSE( test({ "-a", "xxx"            }, ""       ) ); // [unhandled positional]
}

TEST(CmdLineTest, PrefixRequired)
{
    auto test = [](Argv argv, std::string const& a_val) -> bool
    {
        cl::CmdLine cmd("program");

        auto a = cl::makeOption<std::string>(cmd, "a", cl::Prefix, cl::ArgRequired);
        auto b = cl::makeOption<bool>(cmd, "b");

        if (!parse(cmd, std::move(argv)))
            return false;

        if (a.getCount()) EXPECT_EQ(a.get(), a_val);
        return true;
    };

    EXPECT_FALSE( test({ "-a"                   }, ""       ) );
    EXPECT_FALSE( test({ "-a", "-b"             }, ""       ) ); // "-b" is a valid option -> is not a valid argument for -a
    EXPECT_TRUE ( test({ "-a-b"                 }, "-b"     ) );
    EXPECT_TRUE ( test({ "-a=-b"                }, "=-b"    ) );
    EXPECT_TRUE ( test({ "-axxx"                }, "xxx"    ) );
    EXPECT_TRUE ( test({ "-a=xxx"               }, "=xxx"   ) );
    EXPECT_FALSE( test({ "-a", "xxx"            }, "xxx"    ) ); // [option 'a' expects and argument, unhandled positional]
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#define _USE_MATH_DEFINES
#include <cmath>
#include <random>
#include <seastar/core/sstring.hh>
#include "redis/lolwut.hh"
#include "redis/version.hh"

using namespace seastar;

namespace redis {

class canvas {
    const int _width, _height;
    std::unique_ptr<char[]> _pixels;
public:
    canvas(const int width, int height) : _width(width), _height(height), _pixels(std::make_unique<char[]>(width * height)) {}

    void draw_pixel(const int x, const int y, const int color) {
        if (x < 0 || x >= _width || y < 0 || y >= _height) {
            return;
        }
        _pixels[x + y * _width] = color;
    }

    static void translate_pixels_group(const int byte, int8_t *output) {
        const int code = 0x2800 + byte;
        output[0] = 0xe0 | (code >> 12);
        output[1] = 0x80 | ((code >> 6) & 0x3f);
        output[2] = 0x80 | (code & 0x3f);
    }

    int get_pixel(const int x, const int y) {
        if (x < 0 || x >= _width || y < 0 || y >= _height) {
            return 0;
        }
        return _pixels[x + y * _width];
    }

    void draw_line(int x1, int y1, const int x2, const int y2, const int color) {
        const int dx = abs(x2 - x1);
        const int dy = abs(y2 - y1);
        const int sx = (x1 < x2) ? 1 : -1;
        const int sy = (y1 < y2) ? 1 : -1;
        int err = dx - dy;
        int e2;

        while (1) {
            draw_pixel(x1, y1, color);
            if (x1 == x2 && y1 == y2) {
                break;
            }
            e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x1 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y1 += sy;
            }
        }
    }

    void draw_square(const int x, const int y, float size, const float angle) {
        int px[4], py[4];

        size /= 1.4142135623;
        size = round(size);

        float k = M_PI/4 + angle;
        for (int j = 0; j < 4; j++) {
            px[j] = round(sin(k) * size + x);
            py[j] = round(cos(k) * size + y);
            k += M_PI/2;
        }

        for (int j = 0; j < 4; j++) {
            draw_line(px[j], py[j], px[(j + 1) % 4], py[(j + 1) % 4], 1);
        }
    }

    static canvas draw_schotter(const int cols, const int squares_per_row, const int squares_per_col) {
        const int width = cols * 2;
        const int padding = width > 4 ? 2 : 0;
        const float side = static_cast<float>((width - padding * 1) / squares_per_row);
        const int height = side * squares_per_col + padding * 0;
        canvas c {width, height};
        std::random_device seed_gen;
        std::default_random_engine engine(seed_gen());
        std::uniform_real_distribution<float> dist(0.0, 1.0);

        for (int y = 0; y < squares_per_col; y++) {
            for (int x = 0; x < squares_per_row; x++) {
                int sx = x * side + side / 2 + padding;
                int sy = y * side + side / 2 + padding;

                float angle = 0;
                if (y > 1) {
                    float r1 = dist(engine) / squares_per_col * y;
                    float r2 = dist(engine) / squares_per_col * y;
                    float r3 = dist(engine) / squares_per_col * y;
                    if (engine() % 2) {
                        r1 = -r1;
                    }
                    if (engine() % 2) {
                        r2 = -r2;
                    }
                    if (engine() % 2) {
                        r3 = -r3;
                    }
                    angle = r1;
                    sx += r2 * side / 3;
                    sy += r3 * side / 3;
                }
                c.draw_square(sx, sy, side, angle);
            }
        }
        return c;
    }

    bytes render_canvas() {
        // txt size = height * width * pixels 3byte + height * return code 1byte
        bytes txt(bytes::initialized_later(), (_height / 4) * (_width / 2) * 3 + (_height / 4) * 1);
        int i = 0;
        for (int y = 0; y < _height; y += 4) {
            for (int x = 0; x < _width; x += 2) {
                int byte = 0;
                int8_t unicode[3];
                if (get_pixel(x, y)) {
                    byte |= (1 << 0);
                }
                if (get_pixel(x, y + 1)) {
                    byte |= (1 << 1);
                }
                if (get_pixel(x, y + 2)) {
                    byte |= (1 << 2);
                }
                if (get_pixel(x + 1, y)) {
                    byte |= (1 << 3);
                }
                if (get_pixel(x + 1, y + 1)) {
                    byte |= (1 << 4);
                }
                if (get_pixel(x + 1, y + 2)) {
                    byte |= (1 << 5);
                }
                if (get_pixel(x, y + 3)) {
                    byte |= (1 << 6);
                }
                if (get_pixel(x + 1, y + 3)) {
                    byte |= (1 << 7);
                }
                translate_pixels_group(byte, unicode);
                std::memcpy(&txt[i], &unicode[0], 3);
                i += 3;
            }
            if (y != _height - 1) {
                txt[i++] = '\n';
            }
        }
        return txt;
    }

};

future<bytes> lolwut5(const int cols, const int squares_per_row, const int squares_per_col) {
    auto c = canvas::draw_schotter(cols, squares_per_row, squares_per_col);
    auto rendered = c.render_canvas();
    const bytes msg {"\nGeorg Nees - schotter, plotter on paper, 1968. Redis ver. "};
    const bytes nr {"\n"};
    rendered += msg;
    rendered += redis_version;
    rendered += nr;
    return make_ready_future<bytes>(rendered);
}

}<commit_msg>redis: lolwut parameter fix<commit_after>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#define _USE_MATH_DEFINES
#include <cmath>
#include <random>
#include <seastar/core/sstring.hh>
#include "redis/lolwut.hh"
#include "redis/version.hh"

using namespace seastar;

namespace redis {

class canvas {
    const int _width, _height;
    std::unique_ptr<char[]> _pixels;
public:
    canvas(const int width, int height) : _width(width), _height(height), _pixels(std::make_unique<char[]>(width * height)) {}

    void draw_pixel(const int x, const int y, const int color) {
        if (x < 0 || x >= _width || y < 0 || y >= _height) {
            return;
        }
        _pixels[x + y * _width] = color;
    }

    static void translate_pixels_group(const int byte, int8_t *output) {
        const int code = 0x2800 + byte;
        output[0] = 0xe0 | (code >> 12);
        output[1] = 0x80 | ((code >> 6) & 0x3f);
        output[2] = 0x80 | (code & 0x3f);
    }

    int get_pixel(const int x, const int y) {
        if (x < 0 || x >= _width || y < 0 || y >= _height) {
            return 0;
        }
        return _pixels[x + y * _width];
    }

    void draw_line(int x1, int y1, const int x2, const int y2, const int color) {
        const int dx = abs(x2 - x1);
        const int dy = abs(y2 - y1);
        const int sx = (x1 < x2) ? 1 : -1;
        const int sy = (y1 < y2) ? 1 : -1;
        int err = dx - dy;
        int e2;

        while (1) {
            draw_pixel(x1, y1, color);
            if (x1 == x2 && y1 == y2) {
                break;
            }
            e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x1 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y1 += sy;
            }
        }
    }

    void draw_square(const int x, const int y, float size, const float angle) {
        int px[4], py[4];

        size /= 1.4142135623;
        size = round(size);

        float k = M_PI/4 + angle;
        for (int j = 0; j < 4; j++) {
            px[j] = round(sin(k) * size + x);
            py[j] = round(cos(k) * size + y);
            k += M_PI/2;
        }

        for (int j = 0; j < 4; j++) {
            draw_line(px[j], py[j], px[(j + 1) % 4], py[(j + 1) % 4], 1);
        }
    }

    static canvas draw_schotter(const int cols, const int squares_per_row, const int squares_per_col) {
        const int width = cols * 2;
        const int padding = width > 4 ? 2 : 0;
        const float side = static_cast<float>((width - padding * 2) / squares_per_row);
        const int height = side * squares_per_col + padding * 2;
        canvas c {width, height};
        std::random_device seed_gen;
        std::default_random_engine engine(seed_gen());
        std::uniform_real_distribution<float> dist(0.0, 1.0);

        for (int y = 0; y < squares_per_col; y++) {
            for (int x = 0; x < squares_per_row; x++) {
                int sx = x * side + side / 2 + padding;
                int sy = y * side + side / 2 + padding;

                float angle = 0;
                if (y > 1) {
                    float r1 = dist(engine) / squares_per_col * y;
                    float r2 = dist(engine) / squares_per_col * y;
                    float r3 = dist(engine) / squares_per_col * y;
                    if (engine() % 2) {
                        r1 = -r1;
                    }
                    if (engine() % 2) {
                        r2 = -r2;
                    }
                    if (engine() % 2) {
                        r3 = -r3;
                    }
                    angle = r1;
                    sx += r2 * side / 3;
                    sy += r3 * side / 3;
                }
                c.draw_square(sx, sy, side, angle);
            }
        }
        return c;
    }

    bytes render_canvas() {
        // txt size = height * width * pixels 3byte + height * return code 1byte
        bytes txt(bytes::initialized_later(), (_height / 4) * (_width / 2) * 3 + (_height / 4) * 1);
        int i = 0;
        for (int y = 0; y < _height; y += 4) {
            for (int x = 0; x < _width; x += 2) {
                int byte = 0;
                int8_t unicode[3];
                if (get_pixel(x, y)) {
                    byte |= (1 << 0);
                }
                if (get_pixel(x, y + 1)) {
                    byte |= (1 << 1);
                }
                if (get_pixel(x, y + 2)) {
                    byte |= (1 << 2);
                }
                if (get_pixel(x + 1, y)) {
                    byte |= (1 << 3);
                }
                if (get_pixel(x + 1, y + 1)) {
                    byte |= (1 << 4);
                }
                if (get_pixel(x + 1, y + 2)) {
                    byte |= (1 << 5);
                }
                if (get_pixel(x, y + 3)) {
                    byte |= (1 << 6);
                }
                if (get_pixel(x + 1, y + 3)) {
                    byte |= (1 << 7);
                }
                translate_pixels_group(byte, unicode);
                std::memcpy(&txt[i], &unicode[0], 3);
                i += 3;
            }
            if (y != _height - 1) {
                txt[i++] = '\n';
            }
        }
        return txt;
    }

};

future<bytes> lolwut5(const int cols, const int squares_per_row, const int squares_per_col) {
    auto c = canvas::draw_schotter(cols, squares_per_row, squares_per_col);
    auto rendered = c.render_canvas();
    const bytes msg {"\nGeorg Nees - schotter, plotter on paper, 1968. Redis ver. "};
    const bytes nr {"\n"};
    rendered += msg;
    rendered += redis_version;
    rendered += nr;
    return make_ready_future<bytes>(rendered);
}

}<|endoftext|>


<commit_before>// RUN: %clang_cc1 -pedantic -Wall -Wno-comment -verify -fcxx-exceptions -x c++ %s
// RUN: cp %s %t
// RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions -fixit -x c++ %t
// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-comment -fcxx-exceptions -x c++ %t

/* This is a test of the various code modification hints that are
   provided as part of warning or extension diagnostics. All of the
   warnings will be fixed by -fixit, and the resulting file should
   compile cleanly with -Werror -pedantic. */

struct C1 {
  virtual void f();
  static void g();
};
struct C2 : virtual public virtual C1 { }; // expected-error{{duplicate}}

virtual void C1::f() { } // expected-error{{'virtual' can only be specified inside the class definition}}

static void C1::g() { } // expected-error{{'static' can only be specified inside the class definition}}

template<int Value> struct CT { }; // expected-note{{previous use is here}}

CT<10 >> 2> ct; // expected-warning{{require parentheses}}

class C3 {
public:
  C3(C3, int i = 0); // expected-error{{copy constructor must pass its first argument by reference}}
};

struct CT<0> { }; // expected-error{{'template<>'}}

template<> union CT<1> { }; // expected-error{{tag type}}

// Access declarations
class A {
protected:
  int foo();
};

class B : public A {
  A::foo; // expected-warning{{access declarations are deprecated}}
};

void f() throw(); // expected-note{{previous}}
void f(); // expected-warning{{missing exception specification}}

namespace rdar7853795 {
  struct A {
    bool getNumComponents() const; // expected-note{{declared here}}
    void dump() const {
      getNumComponenets(); // expected-error{{use of undeclared identifier 'getNumComponenets'; did you mean 'getNumComponents'?}}
    }
  };
}

namespace rdar7796492 {
  class A { int x, y; A(); };

  A::A()
    : x(1) y(2) { // expected-error{{missing ',' between base or member initializers}}
  }

}

// extra qualification on member
class C {
  int C::foo(); // expected-warning {{extra qualification}}
};

namespace rdar8488464 {
int x = 0;
int x1 &= 0; // expected-error {{invalid '&=' at end of declaration; did you mean '='?}}
int x2 *= 0; // expected-error {{invalid '*=' at end of declaration; did you mean '='?}}
int x3 += 0; // expected-error {{invalid '+=' at end of declaration; did you mean '='?}}
int x4 -= 0; // expected-error {{invalid '-=' at end of declaration; did you mean '='?}}
int x5 != 0; // expected-error {{invalid '!=' at end of declaration; did you mean '='?}}
int x6 /= 0; // expected-error {{invalid '/=' at end of declaration; did you mean '='?}}
int x7 %= 0; // expected-error {{invalid '%=' at end of declaration; did you mean '='?}}
int x8 <= 0; // expected-error {{invalid '<=' at end of declaration; did you mean '='?}}
int x9 <<= 0; // expected-error {{invalid '<<=' at end of declaration; did you mean '='?}}
int x10 >= 0; // expected-error {{invalid '>=' at end of declaration; did you mean '='?}}
int x11 >>= 0; // expected-error {{invalid '>>=' at end of declaration; did you mean '='?}}
int x12 ^= 0; // expected-error {{invalid '^=' at end of declaration; did you mean '='?}}
int x13 |= 0; // expected-error {{invalid '|=' at end of declaration; did you mean '='?}}
int x14 == 0; // expected-error {{invalid '==' at end of declaration; did you mean '='?}}

void f() {
    int x = 0;
    (void)x;
    int x1 &= 0; // expected-error {{invalid '&=' at end of declaration; did you mean '='?}}
    (void)x1;
    int x2 *= 0; // expected-error {{invalid '*=' at end of declaration; did you mean '='?}}
    (void)x2;
    int x3 += 0; // expected-error {{invalid '+=' at end of declaration; did you mean '='?}}
    (void)x3;
    int x4 -= 0; // expected-error {{invalid '-=' at end of declaration; did you mean '='?}}
    (void)x4;
    int x5 != 0; // expected-error {{invalid '!=' at end of declaration; did you mean '='?}}
    (void)x5;
    int x6 /= 0; // expected-error {{invalid '/=' at end of declaration; did you mean '='?}}
    (void)x6;
    int x7 %= 0; // expected-error {{invalid '%=' at end of declaration; did you mean '='?}}
    (void)x7;
    int x8 <= 0; // expected-error {{invalid '<=' at end of declaration; did you mean '='?}}
    (void)x8;
    int x9 <<= 0; // expected-error {{invalid '<<=' at end of declaration; did you mean '='?}}
    (void)x9;
    int x10 >= 0; // expected-error {{invalid '>=' at end of declaration; did you mean '='?}}
    (void)x10;
    int x11 >>= 0; // expected-error {{invalid '>>=' at end of declaration; did you mean '='?}}
    (void)x11;
    int x12 ^= 0; // expected-error {{invalid '^=' at end of declaration; did you mean '='?}}
    (void)x12;
    int x13 |= 0; // expected-error {{invalid '|=' at end of declaration; did you mean '='?}}
    (void)x13;
    int x14 == 0; // expected-error {{invalid '==' at end of declaration; did you mean '='?}}
    (void)x14;
    if (int x = 0)  { (void)x; }
    if (int x1 &= 0) { (void)x1; } // expected-error {{invalid '&=' at end of declaration; did you mean '='?}}
    if (int x2 *= 0) { (void)x2; } // expected-error {{invalid '*=' at end of declaration; did you mean '='?}}
    if (int x3 += 0) { (void)x3; } // expected-error {{invalid '+=' at end of declaration; did you mean '='?}}
    if (int x4 -= 0) { (void)x4; } // expected-error {{invalid '-=' at end of declaration; did you mean '='?}}
    if (int x5 != 0) { (void)x5; } // expected-error {{invalid '!=' at end of declaration; did you mean '='?}}
    if (int x6 /= 0) { (void)x6; } // expected-error {{invalid '/=' at end of declaration; did you mean '='?}}
    if (int x7 %= 0) { (void)x7; } // expected-error {{invalid '%=' at end of declaration; did you mean '='?}}
    if (int x8 <= 0) { (void)x8; } // expected-error {{invalid '<=' at end of declaration; did you mean '='?}}
    if (int x9 <<= 0) { (void)x9; } // expected-error {{invalid '<<=' at end of declaration; did you mean '='?}}
    if (int x10 >= 0) { (void)x10; } // expected-error {{invalid '>=' at end of declaration; did you mean '='?}}
    if (int x11 >>= 0) { (void)x11; } // expected-error {{invalid '>>=' at end of declaration; did you mean '='?}}
    if (int x12 ^= 0) { (void)x12; } // expected-error {{invalid '^=' at end of declaration; did you mean '='?}}
    if (int x13 |= 0) { (void)x13; } // expected-error {{invalid '|=' at end of declaration; did you mean '='?}}
    if (int x14 == 0) { (void)x14; } // expected-error {{invalid '==' at end of declaration; did you mean '='?}}
}
}

template <class A>
class F1 {
public:
  template <int B>
  class Iterator {
  };
};

template<class T>
class F2  {
  typename F1<T>:: /*template*/  Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
};

template <class T>
void f(){
  typename F1<T>:: /*template*/ Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
}

// Tests for &/* fixits radar 7113438.
class AD {};
class BD: public AD {};

void test (BD &br) {
  AD* aPtr;
  BD b;
  aPtr = b; // expected-error {{assigning to 'AD *' from incompatible type 'BD'; take the address with &}}
  aPtr = br; // expected-error {{assigning to 'AD *' from incompatible type 'BD'; take the address with &}}
}

void foo1() const {} // expected-error {{non-member function cannot have 'const' qualifier}}
void foo2() volatile {} // expected-error {{non-member function cannot have 'volatile' qualifier}}
void foo3() const volatile {} // expected-error {{non-member function cannot have 'const volatile' qualifier}}

struct S { void f(int, char); };
int itsAComma,
itsAComma2 = 0,
oopsAComma(42), // expected-error {{expected ';' at end of declaration}}
AD oopsMoreCommas() {
  static int n = 0, // expected-error {{expected ';' at end of declaration}}
  static char c,
  &d = c, // expected-error {{expected ';' at end of declaration}}
  S s, // expected-error {{expected ';' at end of declaration}}
  s.f(n, d);
  AD ad, // expected-error {{expected ';' at end of declaration}}
  return ad;
}
struct MoreAccidentalCommas {
  int a : 5,
      b : 7,
        : 4, // expected-error {{expected ';' at end of declaration}}
  char c, // expected-error {{expected ';' at end of declaration}}
  double d, // expected-error {{expected ';' at end of declaration}}
  MoreAccidentalCommas *next, // expected-error {{expected ';' at end of declaration}}
public:
  int k, // expected-error {{expected ';' at end of declaration}}
  friend void f(MoreAccidentalCommas) {}
  int k2, // expected-error {{expected ';' at end of declaration}}
  virtual void g(), // expected-error {{expected ';' at end of declaration}}
};

template<class T> struct Mystery;
template<class T> typedef Mystery<T>::type getMysteriousThing() { // \
  expected-error {{function definition declared 'typedef'}} \
  expected-error {{missing 'typename' prior to dependent}}
  return Mystery<T>::get();
}

template<template<typename> Foo, // expected-error {{template template parameter requires 'class' after the parameter list}}
         template<typename> typename Bar, // expected-error {{template template parameter requires 'class' after the parameter list}}
         template<typename> struct Baz> // expected-error {{template template parameter requires 'class' after the parameter list}}
void func();

namespace ShadowedTagType {
class Foo {
 public:
  enum Bar { X, Y };
  void SetBar(Bar bar);
  Bar Bar();
 private:
  Bar bar_; // expected-error {{must use 'enum' tag to refer to type 'Bar' in this scope}}
};
void Foo::SetBar(Bar bar) { bar_ = bar; } // expected-error {{must use 'enum' tag to refer to type 'Bar' in this scope}}
<commit_msg>Re-add the closing '}' for the namespace I accidentally deleted when removing a (new) duplicate test whose only difference was the tag type being an enum instead of a struct.<commit_after>// RUN: %clang_cc1 -pedantic -Wall -Wno-comment -verify -fcxx-exceptions -x c++ %s
// RUN: cp %s %t
// RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions -fixit -x c++ %t
// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-comment -fcxx-exceptions -x c++ %t

/* This is a test of the various code modification hints that are
   provided as part of warning or extension diagnostics. All of the
   warnings will be fixed by -fixit, and the resulting file should
   compile cleanly with -Werror -pedantic. */

struct C1 {
  virtual void f();
  static void g();
};
struct C2 : virtual public virtual C1 { }; // expected-error{{duplicate}}

virtual void C1::f() { } // expected-error{{'virtual' can only be specified inside the class definition}}

static void C1::g() { } // expected-error{{'static' can only be specified inside the class definition}}

template<int Value> struct CT { }; // expected-note{{previous use is here}}

CT<10 >> 2> ct; // expected-warning{{require parentheses}}

class C3 {
public:
  C3(C3, int i = 0); // expected-error{{copy constructor must pass its first argument by reference}}
};

struct CT<0> { }; // expected-error{{'template<>'}}

template<> union CT<1> { }; // expected-error{{tag type}}

// Access declarations
class A {
protected:
  int foo();
};

class B : public A {
  A::foo; // expected-warning{{access declarations are deprecated}}
};

void f() throw(); // expected-note{{previous}}
void f(); // expected-warning{{missing exception specification}}

namespace rdar7853795 {
  struct A {
    bool getNumComponents() const; // expected-note{{declared here}}
    void dump() const {
      getNumComponenets(); // expected-error{{use of undeclared identifier 'getNumComponenets'; did you mean 'getNumComponents'?}}
    }
  };
}

namespace rdar7796492 {
  class A { int x, y; A(); };

  A::A()
    : x(1) y(2) { // expected-error{{missing ',' between base or member initializers}}
  }

}

// extra qualification on member
class C {
  int C::foo(); // expected-warning {{extra qualification}}
};

namespace rdar8488464 {
int x = 0;
int x1 &= 0; // expected-error {{invalid '&=' at end of declaration; did you mean '='?}}
int x2 *= 0; // expected-error {{invalid '*=' at end of declaration; did you mean '='?}}
int x3 += 0; // expected-error {{invalid '+=' at end of declaration; did you mean '='?}}
int x4 -= 0; // expected-error {{invalid '-=' at end of declaration; did you mean '='?}}
int x5 != 0; // expected-error {{invalid '!=' at end of declaration; did you mean '='?}}
int x6 /= 0; // expected-error {{invalid '/=' at end of declaration; did you mean '='?}}
int x7 %= 0; // expected-error {{invalid '%=' at end of declaration; did you mean '='?}}
int x8 <= 0; // expected-error {{invalid '<=' at end of declaration; did you mean '='?}}
int x9 <<= 0; // expected-error {{invalid '<<=' at end of declaration; did you mean '='?}}
int x10 >= 0; // expected-error {{invalid '>=' at end of declaration; did you mean '='?}}
int x11 >>= 0; // expected-error {{invalid '>>=' at end of declaration; did you mean '='?}}
int x12 ^= 0; // expected-error {{invalid '^=' at end of declaration; did you mean '='?}}
int x13 |= 0; // expected-error {{invalid '|=' at end of declaration; did you mean '='?}}
int x14 == 0; // expected-error {{invalid '==' at end of declaration; did you mean '='?}}

void f() {
    int x = 0;
    (void)x;
    int x1 &= 0; // expected-error {{invalid '&=' at end of declaration; did you mean '='?}}
    (void)x1;
    int x2 *= 0; // expected-error {{invalid '*=' at end of declaration; did you mean '='?}}
    (void)x2;
    int x3 += 0; // expected-error {{invalid '+=' at end of declaration; did you mean '='?}}
    (void)x3;
    int x4 -= 0; // expected-error {{invalid '-=' at end of declaration; did you mean '='?}}
    (void)x4;
    int x5 != 0; // expected-error {{invalid '!=' at end of declaration; did you mean '='?}}
    (void)x5;
    int x6 /= 0; // expected-error {{invalid '/=' at end of declaration; did you mean '='?}}
    (void)x6;
    int x7 %= 0; // expected-error {{invalid '%=' at end of declaration; did you mean '='?}}
    (void)x7;
    int x8 <= 0; // expected-error {{invalid '<=' at end of declaration; did you mean '='?}}
    (void)x8;
    int x9 <<= 0; // expected-error {{invalid '<<=' at end of declaration; did you mean '='?}}
    (void)x9;
    int x10 >= 0; // expected-error {{invalid '>=' at end of declaration; did you mean '='?}}
    (void)x10;
    int x11 >>= 0; // expected-error {{invalid '>>=' at end of declaration; did you mean '='?}}
    (void)x11;
    int x12 ^= 0; // expected-error {{invalid '^=' at end of declaration; did you mean '='?}}
    (void)x12;
    int x13 |= 0; // expected-error {{invalid '|=' at end of declaration; did you mean '='?}}
    (void)x13;
    int x14 == 0; // expected-error {{invalid '==' at end of declaration; did you mean '='?}}
    (void)x14;
    if (int x = 0)  { (void)x; }
    if (int x1 &= 0) { (void)x1; } // expected-error {{invalid '&=' at end of declaration; did you mean '='?}}
    if (int x2 *= 0) { (void)x2; } // expected-error {{invalid '*=' at end of declaration; did you mean '='?}}
    if (int x3 += 0) { (void)x3; } // expected-error {{invalid '+=' at end of declaration; did you mean '='?}}
    if (int x4 -= 0) { (void)x4; } // expected-error {{invalid '-=' at end of declaration; did you mean '='?}}
    if (int x5 != 0) { (void)x5; } // expected-error {{invalid '!=' at end of declaration; did you mean '='?}}
    if (int x6 /= 0) { (void)x6; } // expected-error {{invalid '/=' at end of declaration; did you mean '='?}}
    if (int x7 %= 0) { (void)x7; } // expected-error {{invalid '%=' at end of declaration; did you mean '='?}}
    if (int x8 <= 0) { (void)x8; } // expected-error {{invalid '<=' at end of declaration; did you mean '='?}}
    if (int x9 <<= 0) { (void)x9; } // expected-error {{invalid '<<=' at end of declaration; did you mean '='?}}
    if (int x10 >= 0) { (void)x10; } // expected-error {{invalid '>=' at end of declaration; did you mean '='?}}
    if (int x11 >>= 0) { (void)x11; } // expected-error {{invalid '>>=' at end of declaration; did you mean '='?}}
    if (int x12 ^= 0) { (void)x12; } // expected-error {{invalid '^=' at end of declaration; did you mean '='?}}
    if (int x13 |= 0) { (void)x13; } // expected-error {{invalid '|=' at end of declaration; did you mean '='?}}
    if (int x14 == 0) { (void)x14; } // expected-error {{invalid '==' at end of declaration; did you mean '='?}}
}
}

template <class A>
class F1 {
public:
  template <int B>
  class Iterator {
  };
};

template<class T>
class F2  {
  typename F1<T>:: /*template*/  Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
};

template <class T>
void f(){
  typename F1<T>:: /*template*/ Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
}

// Tests for &/* fixits radar 7113438.
class AD {};
class BD: public AD {};

void test (BD &br) {
  AD* aPtr;
  BD b;
  aPtr = b; // expected-error {{assigning to 'AD *' from incompatible type 'BD'; take the address with &}}
  aPtr = br; // expected-error {{assigning to 'AD *' from incompatible type 'BD'; take the address with &}}
}

void foo1() const {} // expected-error {{non-member function cannot have 'const' qualifier}}
void foo2() volatile {} // expected-error {{non-member function cannot have 'volatile' qualifier}}
void foo3() const volatile {} // expected-error {{non-member function cannot have 'const volatile' qualifier}}

struct S { void f(int, char); };
int itsAComma,
itsAComma2 = 0,
oopsAComma(42), // expected-error {{expected ';' at end of declaration}}
AD oopsMoreCommas() {
  static int n = 0, // expected-error {{expected ';' at end of declaration}}
  static char c,
  &d = c, // expected-error {{expected ';' at end of declaration}}
  S s, // expected-error {{expected ';' at end of declaration}}
  s.f(n, d);
  AD ad, // expected-error {{expected ';' at end of declaration}}
  return ad;
}
struct MoreAccidentalCommas {
  int a : 5,
      b : 7,
        : 4, // expected-error {{expected ';' at end of declaration}}
  char c, // expected-error {{expected ';' at end of declaration}}
  double d, // expected-error {{expected ';' at end of declaration}}
  MoreAccidentalCommas *next, // expected-error {{expected ';' at end of declaration}}
public:
  int k, // expected-error {{expected ';' at end of declaration}}
  friend void f(MoreAccidentalCommas) {}
  int k2, // expected-error {{expected ';' at end of declaration}}
  virtual void g(), // expected-error {{expected ';' at end of declaration}}
};

template<class T> struct Mystery;
template<class T> typedef Mystery<T>::type getMysteriousThing() { // \
  expected-error {{function definition declared 'typedef'}} \
  expected-error {{missing 'typename' prior to dependent}}
  return Mystery<T>::get();
}

template<template<typename> Foo, // expected-error {{template template parameter requires 'class' after the parameter list}}
         template<typename> typename Bar, // expected-error {{template template parameter requires 'class' after the parameter list}}
         template<typename> struct Baz> // expected-error {{template template parameter requires 'class' after the parameter list}}
void func();

namespace ShadowedTagType {
class Foo {
 public:
  enum Bar { X, Y };
  void SetBar(Bar bar);
  Bar Bar();
 private:
  Bar bar_; // expected-error {{must use 'enum' tag to refer to type 'Bar' in this scope}}
};
void Foo::SetBar(Bar bar) { bar_ = bar; } // expected-error {{must use 'enum' tag to refer to type 'Bar' in this scope}}
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016, Microsoft

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <chrono>
#include <thread>

#include "gtest/gtest.h"

#include "BitFunnel/Utilities/Factories.h"
#include "BitFunnel/Utilities/IThreadManager.h"
#include "LoggerInterfaces/ConsoleLogger.h"
#include "LoggerInterfaces/Logging.h"
#include "TokenManager.h"
#include "TokenTracker.h"

namespace BitFunnel
{
    namespace TokenManagerUnitTest
    {
        TEST(BasicTest, Trivial)
        {
            TokenManager tokenManager;

            {
                const Token token1 = tokenManager.RequestToken();
                ASSERT_EQ(token1.GetSerialNumber(), 0u);

                const Token token2 = tokenManager.RequestToken();
                ASSERT_EQ(token2.GetSerialNumber(), 1u);
            }

            tokenManager.Shutdown();
        }

        TEST(StartTrackerTest, Trivial)
        {
            TokenManager tokenManager;

            // Starting a tracker when there are no tokens in flight.
            const std::shared_ptr<ITokenTracker> noTokensTracker
                = tokenManager.StartTracker();

            ASSERT_TRUE(noTokensTracker->IsComplete());

            // Tracker with token[0] in flight.
            std::shared_ptr<ITokenTracker> token0Tracker;
            {
                const Token token0 = tokenManager.RequestToken();
                ASSERT_TRUE(noTokensTracker->IsComplete());

                token0Tracker = tokenManager.StartTracker();
                ASSERT_TRUE(!(token0Tracker->IsComplete()));
            }

            // token0 goes out of scope, which should make the token0Tracker
            // complete.
            ASSERT_TRUE(token0Tracker->IsComplete());

            // No impact on noTokensTracker.
            ASSERT_TRUE(noTokensTracker->IsComplete());

            // Tracker with token[1] in flight.
            std::shared_ptr<ITokenTracker> token1Tracker;
            {
                const Token token1 = tokenManager.RequestToken();

                token1Tracker = tokenManager.StartTracker();
                ASSERT_TRUE(!(token1Tracker->IsComplete()));

                // Request one more token after a tracker has been started and
                // release it before releasing a previous token. It should not
                // impact the tracker because this token is not of its interest.
                {
                    const Token token2 = tokenManager.RequestToken();
                    ASSERT_TRUE(!(token1Tracker->IsComplete()));
                }

                // token1Tracker is still not complete since token1 has not
                // been returned.
                ASSERT_TRUE(!(token1Tracker->IsComplete()));
            }

            // token1 goes out of scope marking token1Tracker complete.
            ASSERT_TRUE(token1Tracker->IsComplete());

            // No impact on previous trackers.
            ASSERT_TRUE(noTokensTracker->IsComplete());
            ASSERT_TRUE(token0Tracker->IsComplete());

            tokenManager.Shutdown();
        }

        //*********************************************************************
        //
        // Test implementation of ITokenTracker that knows when it is supposed
        // to receive token confirmations and when not.
        //
        //*********************************************************************
        class TestTokenTracker : public ITokenTracker
        {
        public:
            TestTokenTracker(SerialNumber cutoffSerialNumber, unsigned remainingTokenCount);

            bool OnTokenComplete(SerialNumber serialNumber);

            //
            // ITokenTracker API.
            //
            virtual bool IsComplete() const override;
            virtual void WaitForCompletion() override;

        private:
            TokenTracker m_tracker;
            const SerialNumber m_cutoffSerialNumber;
            std::atomic<bool> m_isExpectingTokens;
        };

        TestTokenTracker::TestTokenTracker(SerialNumber cutoffSerialNumber, unsigned remainingTokenCount)
            : m_tracker(cutoffSerialNumber, remainingTokenCount),
              m_cutoffSerialNumber(cutoffSerialNumber),
              m_isExpectingTokens(true)
        {
        }

        bool TestTokenTracker::OnTokenComplete(SerialNumber serialNumber)
        {
            // DESIGN NOTE: we can't ASSERT_TRUE here because that causes a void
            // return, which conflicts with this function's type.
            const bool isExpectingCallbacks = m_isExpectingTokens.load() != 0;
            EXPECT_TRUE(isExpectingCallbacks)
                << serialNumber
                << "was not expected. Cutoff: "
                << m_cutoffSerialNumber;

            const bool hasTrackingComplete = m_tracker.OnTokenComplete(serialNumber);
            if (hasTrackingComplete)
            {
                m_isExpectingTokens = false;
            }

            return hasTrackingComplete;
        }

        bool TestTokenTracker::IsComplete() const
        {
            return m_tracker.IsComplete();
        }

        void TestTokenTracker::WaitForCompletion()
        {
            return m_tracker.WaitForCompletion();
        }

        //*********************************************************************
        //
        // Represents a thread which gets a token from a token manager, holds it
        // for a random amount of time, and releases it.
        //
        //*********************************************************************
        class TokenRequestorThread : public IThreadBase
        {
        public:

            TokenRequestorThread(ITokenManager& tokenManager,
                                 std::atomic<bool>& isRunning);

            virtual void EntryPoint() override;

        private:
            void GetAndReleaseToken();

            // Parent token manager from which to request a token.
            ITokenManager& m_tokenManager;

            // Flag to indicate if the thread should be running.
            std::atomic<bool> & m_isRunning;
        };

        TokenRequestorThread::TokenRequestorThread(ITokenManager& tokenManager,
                                                   std::atomic<bool>& isRunning)
            : m_tokenManager(tokenManager),
              m_isRunning(isRunning)
        {
        }

        void TokenRequestorThread::GetAndReleaseToken()
        {
            const Token token = m_tokenManager.RequestToken();
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }

        void TokenRequestorThread::EntryPoint()
        {
            while (m_isRunning)
            {
                GetAndReleaseToken();
            }
        }

        //*********************************************************************
        //
        // TokenTrackerThread - represent a thread which periodically starts a
        // new token tracker at an arbitrary time and waits for its completion.
        //
        //*********************************************************************
        class TokenTrackerThread : public IThreadBase
        {
        public:
            TokenTrackerThread(ITokenManager& tokenManager,
                               std::atomic<bool> & isRunning);

            ~TokenTrackerThread() {}

            virtual void EntryPoint() override;

        private:

            // Starts a new tracker and waits for its completion.
            void TrackTokens() const;

            ITokenManager& m_tokenManager;
            std::atomic<bool>& m_isRunning;
        };

        TokenTrackerThread::TokenTrackerThread(ITokenManager& tokenManager,
                                               std::atomic<bool>& isRunning)
            : m_tokenManager(tokenManager),
              m_isRunning(isRunning)
        {
        }

        void TokenTrackerThread::EntryPoint()
        {
            while (m_isRunning)
            {
                TrackTokens();
                // 2 is an arbitrary number.
                std::this_thread::sleep_for(std::chrono::milliseconds(2));
            }
        }

        void TokenTrackerThread::TrackTokens() const
        {
            const std::shared_ptr<ITokenTracker> tracker = m_tokenManager.StartTracker();

            // There are 2 holders of the tracker - token manager and this
            // function.
            const long useCount = tracker.use_count();
            // This check was disabled because it can spuriously fail due to
            // variation in thread scheduling.
            // ASSERT_EQ(useCount, 2);

            // static const unsigned c_trackerCompletionTimeoutInMS = 1000; TODO
            tracker->WaitForCompletion();

            // Give the manager some time to de-register a tracker.
            // This is to accommodate the fact that WaitForCompletion() call
            // here and token manager checking the status of the tracker
            // happening on different threads and potentially one can be
            // a little faster than the other.
            // 3 is an arbitrary number.
            std::this_thread::sleep_for(std::chrono::milliseconds(3));

            // The tracker should now be de-registered from the token manager,
            // and this should reduce the use count by 1.
            // This check was disabled because it can spuriously fail due to
            // variation in thread scheduling.
            // ASSERT_EQ(tracker.use_count(), useCount - 1);

            LogB(Logging::Info, "TokenTrackerThread", "Tracker finished",useCount);
        }

        //*********************************************************************
        //
        // TokenThreadHolder - class which simulates multiple threads which
        // request and return tokens from a manager, and optionally, a thread(s)
        // which periodically asks the token manager to start tracking tokens
        // at an arbitraty time.
        //
        //*********************************************************************
        class TokenThreadHolder
        {
        public:
            TokenThreadHolder(unsigned threadCount, ITokenManager& tokenManager);

            ~TokenThreadHolder();

            void AddThread(IThreadBase* thread);

            void Start();

            void Stop();

        private:

            std::vector<IThreadBase*> m_threads;
            std::unique_ptr<IThreadManager> m_threadManager;
            std::atomic <bool> m_isRunning;
        };

        TokenThreadHolder::TokenThreadHolder(unsigned threadCount, ITokenManager& tokenManager)
            : m_isRunning(true)
        {
            m_threads.resize(threadCount);

            for (unsigned i = 0; i < threadCount; ++i)
            {
                m_threads[i] = new TokenRequestorThread(tokenManager, m_isRunning);
            }
        }

        TokenThreadHolder::~TokenThreadHolder()
        {
            for (const auto& thread: m_threads)
            {
                delete thread;
            }
        }

        void
        TokenThreadHolder::AddThread(IThreadBase* thread)
        {
            m_threads.push_back(thread);
        }

        void TokenThreadHolder::Start()
        {
            m_threadManager = Factories::CreateThreadManager(m_threads);
        }

        void TokenThreadHolder::Stop()
        {
            m_isRunning = false;

            // static const unsigned c_threadDrainTimeout = 1000; TODO
            m_threadManager->WaitForThreads();
        }

        // Test which verifies distribution of tokens and starting/stopping
        // token trackers.
        TEST(TokenManagerWithTrackersTest, Trivial)
        {
            static const unsigned c_threadCount = 16;
            // static const unsigned c_testDuration = 5 * 1000; TODO

            TokenManager tokenManager;

            std::unique_ptr<Logging::ILogger> logger(new Logging::ConsoleLogger());
            Logging::RegisterLogger(logger.get());

            TokenThreadHolder tokenDistributor(c_threadCount, tokenManager);

            std::atomic<bool> isRunning(true);

            // Add a thread that periodically starts tracking tokens.
            TokenTrackerThread * trackerThread = new TokenTrackerThread(tokenManager, isRunning);
            tokenDistributor.AddThread(trackerThread);

            tokenDistributor.Start();

            // DESIGN NOTE: this test used to have random sleeps in the methods
            // and a 5 second sleep here. All the sleeps were reduced because we
            // still get some randomness in ordering and this test shouldn't
            // take 5+ seconds to run. However, it's possible that's too extreme
            // and we should larger sleeps.
            // This test, with the reduced sleeps, found a threading bug that
            // was triggered every 10s to 100s of executions. With the old version
            // of this test, that would have taken an unreasonably long time, so
            // even if this test is sacrificing some "randomness" by having
            // a shorter duration, I believe it makes up for it by the increased
            // number of executions possible per unit time, and that we should
            // really have a seperate test that's designed for overnight use
            // that can be more comprehensive.
            // 5 is an arbitrary number that's > 2x 2.
            std::this_thread::sleep_for(std::chrono::milliseconds(5));

            isRunning = false;
            tokenDistributor.Stop();

            {
                const Token lastToken = tokenManager.RequestToken();

                // Sanity check that some tokens have actually been issued.
                ASSERT_TRUE(lastToken.GetSerialNumber() > 0);

                LogB(Logging::Info,
                     "TokenManagerWithTrackersTest",
                     "Last serial num: %u",
                     lastToken.GetSerialNumber());
            }

            tokenManager.Shutdown();
        }

        void GetAndHoldToken(ITokenManager& tokenManager,
                             std::atomic<bool>& hasRequested,
                             std::atomic<bool>& isExiting)
        {
            const Token token = tokenManager.RequestToken();
            hasRequested = true;
            // 2 is an arbitrary number.
            std::this_thread::sleep_for(std::chrono::milliseconds(2));
            isExiting = true;
        }

        TEST(ShutdownTest, Trivial)
        {
            TokenManager tokenManager;

            std::atomic<bool> thread1Requested;
            thread1Requested = false;
            std::atomic<bool> thread1Exiting;
            thread1Exiting = false;
            std::thread t1(GetAndHoldToken,
                           std::ref(tokenManager),
                           std::ref(thread1Requested),
                           std::ref(thread1Exiting));
            std::atomic<bool> thread2Requested;
            thread2Requested = false;
            std::atomic<bool> thread2Exiting;
            thread2Exiting = false;
            std::thread t2(GetAndHoldToken,
                           std::ref(tokenManager),
                           std::ref(thread2Requested),
                           std::ref(thread2Exiting));

            // Give threads a chance to get tokens. Check that threads have
            // started but not yet exited.
            while (!thread1Requested) {}
            while (!thread2Requested) {}
            ASSERT_TRUE(!thread1Exiting);
            ASSERT_TRUE(!thread2Exiting);

            tokenManager.Shutdown();

            // TODO: add timeout.
            while (!thread1Exiting) {}
            while (!thread2Exiting) {}

            t1.join();
            t2.join();
        }
    }
}
<commit_msg>Remove possible source of test flakiness.<commit_after>// The MIT License (MIT)

// Copyright (c) 2016, Microsoft

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <chrono>
#include <thread>

#include "gtest/gtest.h"

#include "BitFunnel/Utilities/Factories.h"
#include "BitFunnel/Utilities/IThreadManager.h"
#include "LoggerInterfaces/ConsoleLogger.h"
#include "LoggerInterfaces/Logging.h"
#include "TokenManager.h"
#include "TokenTracker.h"

namespace BitFunnel
{
    namespace TokenManagerUnitTest
    {
        TEST(BasicTest, Trivial)
        {
            TokenManager tokenManager;

            {
                const Token token1 = tokenManager.RequestToken();
                ASSERT_EQ(token1.GetSerialNumber(), 0u);

                const Token token2 = tokenManager.RequestToken();
                ASSERT_EQ(token2.GetSerialNumber(), 1u);
            }

            tokenManager.Shutdown();
        }

        TEST(StartTrackerTest, Trivial)
        {
            TokenManager tokenManager;

            // Starting a tracker when there are no tokens in flight.
            const std::shared_ptr<ITokenTracker> noTokensTracker
                = tokenManager.StartTracker();

            ASSERT_TRUE(noTokensTracker->IsComplete());

            // Tracker with token[0] in flight.
            std::shared_ptr<ITokenTracker> token0Tracker;
            {
                const Token token0 = tokenManager.RequestToken();
                ASSERT_TRUE(noTokensTracker->IsComplete());

                token0Tracker = tokenManager.StartTracker();
                ASSERT_TRUE(!(token0Tracker->IsComplete()));
            }

            // token0 goes out of scope, which should make the token0Tracker
            // complete.
            ASSERT_TRUE(token0Tracker->IsComplete());

            // No impact on noTokensTracker.
            ASSERT_TRUE(noTokensTracker->IsComplete());

            // Tracker with token[1] in flight.
            std::shared_ptr<ITokenTracker> token1Tracker;
            {
                const Token token1 = tokenManager.RequestToken();

                token1Tracker = tokenManager.StartTracker();
                ASSERT_TRUE(!(token1Tracker->IsComplete()));

                // Request one more token after a tracker has been started and
                // release it before releasing a previous token. It should not
                // impact the tracker because this token is not of its interest.
                {
                    const Token token2 = tokenManager.RequestToken();
                    ASSERT_TRUE(!(token1Tracker->IsComplete()));
                }

                // token1Tracker is still not complete since token1 has not
                // been returned.
                ASSERT_TRUE(!(token1Tracker->IsComplete()));
            }

            // token1 goes out of scope marking token1Tracker complete.
            ASSERT_TRUE(token1Tracker->IsComplete());

            // No impact on previous trackers.
            ASSERT_TRUE(noTokensTracker->IsComplete());
            ASSERT_TRUE(token0Tracker->IsComplete());

            tokenManager.Shutdown();
        }

        //*********************************************************************
        //
        // Test implementation of ITokenTracker that knows when it is supposed
        // to receive token confirmations and when not.
        //
        //*********************************************************************
        class TestTokenTracker : public ITokenTracker
        {
        public:
            TestTokenTracker(SerialNumber cutoffSerialNumber, unsigned remainingTokenCount);

            bool OnTokenComplete(SerialNumber serialNumber);

            //
            // ITokenTracker API.
            //
            virtual bool IsComplete() const override;
            virtual void WaitForCompletion() override;

        private:
            TokenTracker m_tracker;
            const SerialNumber m_cutoffSerialNumber;
            std::atomic<bool> m_isExpectingTokens;
        };

        TestTokenTracker::TestTokenTracker(SerialNumber cutoffSerialNumber, unsigned remainingTokenCount)
            : m_tracker(cutoffSerialNumber, remainingTokenCount),
              m_cutoffSerialNumber(cutoffSerialNumber),
              m_isExpectingTokens(true)
        {
        }

        bool TestTokenTracker::OnTokenComplete(SerialNumber serialNumber)
        {
            // DESIGN NOTE: we can't ASSERT_TRUE here because that causes a void
            // return, which conflicts with this function's type.
            const bool isExpectingCallbacks = m_isExpectingTokens.load() != 0;
            EXPECT_TRUE(isExpectingCallbacks)
                << serialNumber
                << "was not expected. Cutoff: "
                << m_cutoffSerialNumber;

            const bool hasTrackingComplete = m_tracker.OnTokenComplete(serialNumber);
            if (hasTrackingComplete)
            {
                m_isExpectingTokens = false;
            }

            return hasTrackingComplete;
        }

        bool TestTokenTracker::IsComplete() const
        {
            return m_tracker.IsComplete();
        }

        void TestTokenTracker::WaitForCompletion()
        {
            return m_tracker.WaitForCompletion();
        }

        //*********************************************************************
        //
        // Represents a thread which gets a token from a token manager, holds it
        // for a random amount of time, and releases it.
        //
        //*********************************************************************
        class TokenRequestorThread : public IThreadBase
        {
        public:

            TokenRequestorThread(ITokenManager& tokenManager,
                                 std::atomic<bool>& isRunning);

            virtual void EntryPoint() override;

        private:
            void GetAndReleaseToken();

            // Parent token manager from which to request a token.
            ITokenManager& m_tokenManager;

            // Flag to indicate if the thread should be running.
            std::atomic<bool> & m_isRunning;
        };

        TokenRequestorThread::TokenRequestorThread(ITokenManager& tokenManager,
                                                   std::atomic<bool>& isRunning)
            : m_tokenManager(tokenManager),
              m_isRunning(isRunning)
        {
        }

        void TokenRequestorThread::GetAndReleaseToken()
        {
            const Token token = m_tokenManager.RequestToken();
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }

        void TokenRequestorThread::EntryPoint()
        {
            while (m_isRunning)
            {
                GetAndReleaseToken();
            }
        }

        //*********************************************************************
        //
        // TokenTrackerThread - represent a thread which periodically starts a
        // new token tracker at an arbitrary time and waits for its completion.
        //
        //*********************************************************************
        class TokenTrackerThread : public IThreadBase
        {
        public:
            TokenTrackerThread(ITokenManager& tokenManager,
                               std::atomic<bool> & isRunning);

            ~TokenTrackerThread() {}

            virtual void EntryPoint() override;

        private:

            // Starts a new tracker and waits for its completion.
            void TrackTokens() const;

            ITokenManager& m_tokenManager;
            std::atomic<bool>& m_isRunning;
        };

        TokenTrackerThread::TokenTrackerThread(ITokenManager& tokenManager,
                                               std::atomic<bool>& isRunning)
            : m_tokenManager(tokenManager),
              m_isRunning(isRunning)
        {
        }

        void TokenTrackerThread::EntryPoint()
        {
            while (m_isRunning)
            {
                TrackTokens();
                // 2 is an arbitrary number.
                std::this_thread::sleep_for(std::chrono::milliseconds(2));
            }
        }

        void TokenTrackerThread::TrackTokens() const
        {
            const std::shared_ptr<ITokenTracker> tracker = m_tokenManager.StartTracker();

            // There are 2 holders of the tracker - token manager and this
            // function.
            const long useCount = tracker.use_count();
            // This check was disabled because it can spuriously fail due to
            // variation in thread scheduling.
            // ASSERT_EQ(useCount, 2);

            // static const unsigned c_trackerCompletionTimeoutInMS = 1000; TODO
            tracker->WaitForCompletion();

            // Give the manager some time to de-register a tracker.
            // This is to accommodate the fact that WaitForCompletion() call
            // here and token manager checking the status of the tracker
            // happening on different threads and potentially one can be
            // a little faster than the other.
            // 3 is an arbitrary number.
            std::this_thread::sleep_for(std::chrono::milliseconds(3));

            // The tracker should now be de-registered from the token manager,
            // and this should reduce the use count by 1.
            // This check was disabled because it can spuriously fail due to
            // variation in thread scheduling.
            // ASSERT_EQ(tracker.use_count(), useCount - 1);

            LogB(Logging::Info, "TokenTrackerThread", "Tracker finished",useCount);
        }

        //*********************************************************************
        //
        // TokenThreadHolder - class which simulates multiple threads which
        // request and return tokens from a manager, and optionally, a thread(s)
        // which periodically asks the token manager to start tracking tokens
        // at an arbitraty time.
        //
        //*********************************************************************
        class TokenThreadHolder
        {
        public:
            TokenThreadHolder(unsigned threadCount, ITokenManager& tokenManager);

            ~TokenThreadHolder();

            void AddThread(IThreadBase* thread);

            void Start();

            void Stop();

        private:

            std::vector<IThreadBase*> m_threads;
            std::unique_ptr<IThreadManager> m_threadManager;
            std::atomic <bool> m_isRunning;
        };

        TokenThreadHolder::TokenThreadHolder(unsigned threadCount, ITokenManager& tokenManager)
            : m_isRunning(true)
        {
            m_threads.resize(threadCount);

            for (unsigned i = 0; i < threadCount; ++i)
            {
                m_threads[i] = new TokenRequestorThread(tokenManager, m_isRunning);
            }
        }

        TokenThreadHolder::~TokenThreadHolder()
        {
            for (const auto& thread: m_threads)
            {
                delete thread;
            }
        }

        void
        TokenThreadHolder::AddThread(IThreadBase* thread)
        {
            m_threads.push_back(thread);
        }

        void TokenThreadHolder::Start()
        {
            m_threadManager = Factories::CreateThreadManager(m_threads);
        }

        void TokenThreadHolder::Stop()
        {
            m_isRunning = false;

            // static const unsigned c_threadDrainTimeout = 1000; TODO
            m_threadManager->WaitForThreads();
        }

        // Test which verifies distribution of tokens and starting/stopping
        // token trackers.
        TEST(TokenManagerWithTrackersTest, Trivial)
        {
            static const unsigned c_threadCount = 16;
            // static const unsigned c_testDuration = 5 * 1000; TODO

            TokenManager tokenManager;

            std::unique_ptr<Logging::ILogger> logger(new Logging::ConsoleLogger());
            Logging::RegisterLogger(logger.get());

            TokenThreadHolder tokenDistributor(c_threadCount, tokenManager);

            std::atomic<bool> isRunning(true);

            // Add a thread that periodically starts tracking tokens.
            TokenTrackerThread * trackerThread = new TokenTrackerThread(tokenManager, isRunning);
            tokenDistributor.AddThread(trackerThread);

            tokenDistributor.Start();

            // DESIGN NOTE: this test used to have random sleeps in the methods
            // and a 5 second sleep here. All the sleeps were reduced because we
            // still get some randomness in ordering and this test shouldn't
            // take 5+ seconds to run. However, it's possible that's too extreme
            // and we should larger sleeps.
            // This test, with the reduced sleeps, found a threading bug that
            // was triggered every 10s to 100s of executions. With the old version
            // of this test, that would have taken an unreasonably long time, so
            // even if this test is sacrificing some "randomness" by having
            // a shorter duration, I believe it makes up for it by the increased
            // number of executions possible per unit time, and that we should
            // really have a seperate test that's designed for overnight use
            // that can be more comprehensive.
            // 5 is an arbitrary number that's > 2x 2.
            std::this_thread::sleep_for(std::chrono::milliseconds(5));

            isRunning = false;
            tokenDistributor.Stop();

            {
                const Token lastToken = tokenManager.RequestToken();

                // Sanity check that some tokens have actually been issued.
                ASSERT_TRUE(lastToken.GetSerialNumber() > 0);

                LogB(Logging::Info,
                     "TokenManagerWithTrackersTest",
                     "Last serial num: %u",
                     lastToken.GetSerialNumber());
            }

            tokenManager.Shutdown();
        }

        void GetAndHoldToken(ITokenManager& tokenManager,
                             std::atomic<bool>& hasRequested,
                             std::atomic<bool>& isExiting)
        {
            const Token token = tokenManager.RequestToken();
            hasRequested = true;
            // 2 is an arbitrary number.
            std::this_thread::sleep_for(std::chrono::milliseconds(2));
            isExiting = true;
        }

        TEST(ShutdownTest, Trivial)
        {
            TokenManager tokenManager;

            std::atomic<bool> thread1Requested;
            thread1Requested = false;
            std::atomic<bool> thread1Exiting;
            thread1Exiting = false;
            std::thread t1(GetAndHoldToken,
                           std::ref(tokenManager),
                           std::ref(thread1Requested),
                           std::ref(thread1Exiting));
            std::atomic<bool> thread2Requested;
            thread2Requested = false;
            std::atomic<bool> thread2Exiting;
            thread2Exiting = false;
            std::thread t2(GetAndHoldToken,
                           std::ref(tokenManager),
                           std::ref(thread2Requested),
                           std::ref(thread2Exiting));

            // Give threads a chance to get tokens. Check that threads have
            // started but not yet exited.
            while (!thread1Requested) {}
            while (!thread2Requested) {}
            // Can't really check for these reliably. TODO: use condition var
            // if we want to check for this.
            // ASSERT_TRUE(!thread1Exiting);
            // ASSERT_TRUE(!thread2Exiting);

            tokenManager.Shutdown();

            // TODO: add timeout.
            while (!thread1Exiting) {}
            while (!thread2Exiting) {}

            t1.join();
            t2.join();
        }
    }
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: OutlineView.hxx,v $
 *
 *  $Revision: 1.15 $
 *
 *  last change: $Author: rt $ $Date: 2008-03-12 11:42:11 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef SD_OUTLINE_VIEW_HXX
#define SD_OUTLINE_VIEW_HXX

#ifndef SD_VIEW_HXX
#include "View.hxx"
#endif

class SdPage;
class SdrPage;
class EditStatus;
class Paragraph;
class SdrTextObj;
class Outliner;
class SfxProgress;

namespace sd { namespace tools {
class EventMultiplexerEvent;
} }

namespace sd {

class DrawDocShell;
class OutlineViewShell;
class OutlineViewModelChangeGuard;
class DrawDocShell;

static const int MAX_OUTLINERVIEWS = 4;

/*************************************************************************
|*
|* Ableitung von ::sd::View fuer den Outline-Modus
|*
\************************************************************************/

class OutlineView
    : public ::sd::View
{
    friend class OutlineViewModelChangeGuard;
public:
    OutlineView (DrawDocShell* pDocSh,
        ::Window* pWindow,
        OutlineViewShell* pOutlineViewSh);
    ~OutlineView (void);

    /** This method is called by the view shell that owns the view to tell
        the view that it can safely connect to the application.
        This method must not be called before the view shell is on the shell
        stack.
    */
    void ConnectToApplication (void);
    void DisconnectFromApplication (void);

    TYPEINFO();

    SdrTextObj*     GetTitleTextObject(SdrPage* pPage);
    SdrTextObj*     GetOutlineTextObject(SdrPage* pPage);

    SdrTextObj*     CreateTitleTextObject(SdPage* pPage);
    SdrTextObj*     CreateOutlineTextObject(SdPage* pPage);

    virtual void AddWindowToPaintView(OutputDevice* pWin);
    virtual void DeleteWindowFromPaintView(OutputDevice* pWin);

    OutlinerView*   GetViewByWindow (::Window* pWin) const;
    SdrOutliner*    GetOutliner() { return(mpOutliner) ; }

    Paragraph*      GetPrevTitle(const Paragraph* pPara);
    Paragraph*      GetNextTitle(const Paragraph* pPara);
    SdPage*         GetActualPage();
    SdPage*         GetPageForParagraph( ::Outliner* pOutl, Paragraph* pPara );
    Paragraph*      GetParagraphForPage( ::Outliner* pOutl, SdPage* pPage );

    /** selects the paragraph for the given page at the outliner view*/
    void            SetActualPage( SdPage* pActual );

    virtual void Paint (const Rectangle& rRect, ::sd::Window* pWin);
    virtual void AdjustPosSizePixel(
        const Point &rPos,
        const Size &rSize,
        ::sd::Window* pWindow);

                    // Callbacks fuer LINKs
    DECL_LINK( ParagraphInsertedHdl, Outliner * );
    DECL_LINK( ParagraphRemovingHdl, Outliner * );
    DECL_LINK( DepthChangedHdl, Outliner * );
    DECL_LINK( StatusEventHdl, EditStatus * );
    DECL_LINK( BeginMovingHdl, Outliner * );
    DECL_LINK( EndMovingHdl, Outliner * );
    DECL_LINK( RemovingPagesHdl, OutlinerView * );
    DECL_LINK( IndentingPagesHdl, OutlinerView * );
    DECL_LINK( BeginDropHdl, void * );
    DECL_LINK( EndDropHdl, void * );

    ULONG         GetPaperWidth() const { return 21000; }  // DIN A4 Breite

    BOOL          PrepareClose(BOOL bUI = TRUE);

    virtual BOOL    GetAttributes( SfxItemSet& rTargetSet, BOOL bOnlyHardAttr=FALSE ) const;
    virtual BOOL    SetAttributes(const SfxItemSet& rSet, BOOL bReplaceAll = FALSE);

//  virtual BOOL       HasMarkedObjUnused() const;
    void               FillOutliner();
    void               SetLinks();
    void               ResetLinks() const;

    SfxStyleSheet*     GetStyleSheet() const;

    void               SetSelectedPages();

    virtual sal_Int8 AcceptDrop (
        const AcceptDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);
    virtual sal_Int8 ExecuteDrop (
        const ExecuteDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);

    // #97766# Re-implement GetScriptType for this view to get correct results
    virtual sal_uInt16 GetScriptType() const;

    /** After this method has been called with <TRUE/> following changes of
        the current page are ignored in that the corresponding text is not
        selected.
        This is used to supress unwanted side effects between selection and
        cursor position.
    */
    void IgnoreCurrentPageChanges (bool bIgnore);

private:
    /** call this method before you do anything that can modify the outliner
        and or the drawing document model. It will create needed undo actions */
    void BeginModelChange();

    /** call this method after BeginModelChange(), when all possible model
        changes are done. */
    void EndModelChange();

    /** merge edit engine undo actions if possible */
    void TryToMergeUndoActions();

    /** updates all changes in the outliner model to the draw model */
    void UpdateDocument();

    /** creates and inserts an empty slide for the given paragraph */
    SdPage* InsertSlideForParagraph( Paragraph* pPara );

    OutlineViewShell*   mpOutlineViewShell;
    SdrOutliner*        mpOutliner;
    OutlinerView*       mpOutlinerView[MAX_OUTLINERVIEWS];

    List*               mpOldParaOrder;
    List*               mpSelectedParas;

    USHORT              mnPagesToProcess;    // fuer die Fortschrittsanzeige
    USHORT              mnPagesProcessed;

    BOOL                mbFirstPaint;

    SfxProgress*        mpProgress;

    /** stores the last used high contrast mode.
        this is changed in onUpdateStyleSettings()
    */
    bool mbHighContrastMode;

    /** stores the last used document color.
        this is changed in onUpdateStyleSettings()
    */
    Color maDocColor;

    /** updates the high contrast settings and document color if they changed.
        @param bForceUpdate forces the method to set all style settings
    */
    void onUpdateStyleSettings( bool bForceUpdate = false );

    /** this link is called from the vcl applicaten when the stylesettings
        change. Its only purpose is to call onUpdateStyleSettings() then.
    */
    DECL_LINK( AppEventListenerHdl, void * );

    DECL_LINK(EventMultiplexerListener, sd::tools::EventMultiplexerEvent*);

    /** holds a model guard during drag and drop between BeginMovingHdl and EndMovingHdl */
    std::auto_ptr< OutlineViewModelChangeGuard > maDragAndDropModelGuard;
};

// calls IgnoreCurrentPageChangesLevel with true in ctor and with false in dtor
class OutlineViewPageChangesGuard
{
public:
    OutlineViewPageChangesGuard( OutlineView* pView );
    ~OutlineViewPageChangesGuard();
private:
    OutlineView* mpView;
};

// calls BeginModelChange() on c'tor and EndModelChange() on d'tor
class OutlineViewModelChangeGuard
{
public:
    OutlineViewModelChangeGuard( OutlineView& rView );
    ~OutlineViewModelChangeGuard();
private:
    OutlineView& mrView;
};

} // end of namespace sd

#endif
<commit_msg>INTEGRATION: CWS changefileheader (1.15.16); FILE MERGED 2008/04/01 12:39:01 thb 1.15.16.2: #i85898# Stripping all external header guards 2008/03/31 13:58:10 rt 1.15.16.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: OutlineView.hxx,v $
 * $Revision: 1.16 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef SD_OUTLINE_VIEW_HXX
#define SD_OUTLINE_VIEW_HXX

#include "View.hxx"

class SdPage;
class SdrPage;
class EditStatus;
class Paragraph;
class SdrTextObj;
class Outliner;
class SfxProgress;

namespace sd { namespace tools {
class EventMultiplexerEvent;
} }

namespace sd {

class DrawDocShell;
class OutlineViewShell;
class OutlineViewModelChangeGuard;
class DrawDocShell;

static const int MAX_OUTLINERVIEWS = 4;

/*************************************************************************
|*
|* Ableitung von ::sd::View fuer den Outline-Modus
|*
\************************************************************************/

class OutlineView
    : public ::sd::View
{
    friend class OutlineViewModelChangeGuard;
public:
    OutlineView (DrawDocShell* pDocSh,
        ::Window* pWindow,
        OutlineViewShell* pOutlineViewSh);
    ~OutlineView (void);

    /** This method is called by the view shell that owns the view to tell
        the view that it can safely connect to the application.
        This method must not be called before the view shell is on the shell
        stack.
    */
    void ConnectToApplication (void);
    void DisconnectFromApplication (void);

    TYPEINFO();

    SdrTextObj*     GetTitleTextObject(SdrPage* pPage);
    SdrTextObj*     GetOutlineTextObject(SdrPage* pPage);

    SdrTextObj*     CreateTitleTextObject(SdPage* pPage);
    SdrTextObj*     CreateOutlineTextObject(SdPage* pPage);

    virtual void AddWindowToPaintView(OutputDevice* pWin);
    virtual void DeleteWindowFromPaintView(OutputDevice* pWin);

    OutlinerView*   GetViewByWindow (::Window* pWin) const;
    SdrOutliner*    GetOutliner() { return(mpOutliner) ; }

    Paragraph*      GetPrevTitle(const Paragraph* pPara);
    Paragraph*      GetNextTitle(const Paragraph* pPara);
    SdPage*         GetActualPage();
    SdPage*         GetPageForParagraph( ::Outliner* pOutl, Paragraph* pPara );
    Paragraph*      GetParagraphForPage( ::Outliner* pOutl, SdPage* pPage );

    /** selects the paragraph for the given page at the outliner view*/
    void            SetActualPage( SdPage* pActual );

    virtual void Paint (const Rectangle& rRect, ::sd::Window* pWin);
    virtual void AdjustPosSizePixel(
        const Point &rPos,
        const Size &rSize,
        ::sd::Window* pWindow);

                    // Callbacks fuer LINKs
    DECL_LINK( ParagraphInsertedHdl, Outliner * );
    DECL_LINK( ParagraphRemovingHdl, Outliner * );
    DECL_LINK( DepthChangedHdl, Outliner * );
    DECL_LINK( StatusEventHdl, EditStatus * );
    DECL_LINK( BeginMovingHdl, Outliner * );
    DECL_LINK( EndMovingHdl, Outliner * );
    DECL_LINK( RemovingPagesHdl, OutlinerView * );
    DECL_LINK( IndentingPagesHdl, OutlinerView * );
    DECL_LINK( BeginDropHdl, void * );
    DECL_LINK( EndDropHdl, void * );

    ULONG         GetPaperWidth() const { return 21000; }  // DIN A4 Breite

    BOOL          PrepareClose(BOOL bUI = TRUE);

    virtual BOOL    GetAttributes( SfxItemSet& rTargetSet, BOOL bOnlyHardAttr=FALSE ) const;
    virtual BOOL    SetAttributes(const SfxItemSet& rSet, BOOL bReplaceAll = FALSE);

//  virtual BOOL       HasMarkedObjUnused() const;
    void               FillOutliner();
    void               SetLinks();
    void               ResetLinks() const;

    SfxStyleSheet*     GetStyleSheet() const;

    void               SetSelectedPages();

    virtual sal_Int8 AcceptDrop (
        const AcceptDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);
    virtual sal_Int8 ExecuteDrop (
        const ExecuteDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);

    // #97766# Re-implement GetScriptType for this view to get correct results
    virtual sal_uInt16 GetScriptType() const;

    /** After this method has been called with <TRUE/> following changes of
        the current page are ignored in that the corresponding text is not
        selected.
        This is used to supress unwanted side effects between selection and
        cursor position.
    */
    void IgnoreCurrentPageChanges (bool bIgnore);

private:
    /** call this method before you do anything that can modify the outliner
        and or the drawing document model. It will create needed undo actions */
    void BeginModelChange();

    /** call this method after BeginModelChange(), when all possible model
        changes are done. */
    void EndModelChange();

    /** merge edit engine undo actions if possible */
    void TryToMergeUndoActions();

    /** updates all changes in the outliner model to the draw model */
    void UpdateDocument();

    /** creates and inserts an empty slide for the given paragraph */
    SdPage* InsertSlideForParagraph( Paragraph* pPara );

    OutlineViewShell*   mpOutlineViewShell;
    SdrOutliner*        mpOutliner;
    OutlinerView*       mpOutlinerView[MAX_OUTLINERVIEWS];

    List*               mpOldParaOrder;
    List*               mpSelectedParas;

    USHORT              mnPagesToProcess;    // fuer die Fortschrittsanzeige
    USHORT              mnPagesProcessed;

    BOOL                mbFirstPaint;

    SfxProgress*        mpProgress;

    /** stores the last used high contrast mode.
        this is changed in onUpdateStyleSettings()
    */
    bool mbHighContrastMode;

    /** stores the last used document color.
        this is changed in onUpdateStyleSettings()
    */
    Color maDocColor;

    /** updates the high contrast settings and document color if they changed.
        @param bForceUpdate forces the method to set all style settings
    */
    void onUpdateStyleSettings( bool bForceUpdate = false );

    /** this link is called from the vcl applicaten when the stylesettings
        change. Its only purpose is to call onUpdateStyleSettings() then.
    */
    DECL_LINK( AppEventListenerHdl, void * );

    DECL_LINK(EventMultiplexerListener, sd::tools::EventMultiplexerEvent*);

    /** holds a model guard during drag and drop between BeginMovingHdl and EndMovingHdl */
    std::auto_ptr< OutlineViewModelChangeGuard > maDragAndDropModelGuard;
};

// calls IgnoreCurrentPageChangesLevel with true in ctor and with false in dtor
class OutlineViewPageChangesGuard
{
public:
    OutlineViewPageChangesGuard( OutlineView* pView );
    ~OutlineViewPageChangesGuard();
private:
    OutlineView* mpView;
};

// calls BeginModelChange() on c'tor and EndModelChange() on d'tor
class OutlineViewModelChangeGuard
{
public:
    OutlineViewModelChangeGuard( OutlineView& rView );
    ~OutlineViewModelChangeGuard();
private:
    OutlineView& mrView;
};

} // end of namespace sd

#endif
<|endoftext|>