<commit_before>#include "catch.hpp"
#include "util.h"
#include <shl_exception.h>
#include <tokenizer.h>
#include <grammar_loader.h>

using namespace shl;

TEST_CASE("Tokenizer Tests") {
    Tokenizer tokenizer;
    GrammarLoader loader;

    SECTION("can load grammar and tokenizer string") {
        string data = load_string("fixture/hello.json");
        string source = "hello world!";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 4 );
        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.hello" );

        REQUIRE( tokens[1].first.substr(source) == "hello" );
        REQUIRE( tokens[1].second.name() == "source.hello prefix.hello" );

        REQUIRE( tokens[2].first.substr(source) == "world!" );
        REQUIRE( tokens[2].second.name() == "source.hello suffix.hello" );

        REQUIRE( tokens[3].first.substr(source) == "!" );
        REQUIRE( tokens[3].second.name() == "source.hello suffix.hello emphasis.hello" );
    }

    SECTION("return a single token when matches a single pattern with no capture groups") {
        string data = load_string("fixture/coffee-script.json");
        string source = "return";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 2 );
        REQUIRE( tokens[1].first.substr(source) == "return" );
        REQUIRE( tokens[1].second.name() == "source.coffee keyword.control.coffee" );
    }

    SECTION("return several tokens when matches a single pattern with capture gorups") {
        string data = load_string("fixture/coffee-script.json");
        string source = "new foo.bar.Baz";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 4 );

        REQUIRE( tokens[1].first.substr(source) == source );
        REQUIRE( tokens[1].second.name() == "source.coffee meta.class.instance.constructor" );

        REQUIRE( tokens[2].first.substr(source) == "new" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.class.instance.constructor keyword.operator.new.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "foo.bar.Baz" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.class.instance.constructor entity.name.type.instance.coffee" );
    }

    SECTION("return grammar top level token when no match at all") {
        string data = load_string("fixture/text.json");
        string source = "   ";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 1 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "text.plain" );
    }

    SECTION("will throw when scope is not properly closed (i.e. source code is malformed)") {
        string data = load_string("fixture/c.json");
        string source = "int main() {";
        Grammar g = loader.load(data);

        REQUIRE_THROWS_AS(tokenizer.tokenize(g, source), InvalidSourceException);
    }

    SECTION("will not throw when scope is not properly closed, if OPTION_TOLERATE_ERROR is specified") {
        string data = load_string("fixture/c.json");
        string source = "int main() {";
        Grammar g = loader.load(data);
        Tokenizer tokenizer(Tokenizer::OPTION_TOLERATE_ERROR);

        REQUIRE_NOTHROW( tokenizer.tokenize(g, source) );
    }

    SECTION("the enclosing scope will cover the sub-scopes") {
        string data = load_string("fixture/coffee-script.json");
        string source = " return new foo.bar.Baz ";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 5 );
        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == "return" );
        REQUIRE( tokens[1].second.name() == "source.coffee keyword.control.coffee" );

        REQUIRE( tokens[2].first.substr(source) == "new foo.bar.Baz" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.class.instance.constructor" );

        REQUIRE( tokens[3].first.substr(source) == "new" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.class.instance.constructor keyword.operator.new.coffee" );

        REQUIRE( tokens[4].first.substr(source) == "foo.bar.Baz" );
        REQUIRE( tokens[4].second.name() == "source.coffee meta.class.instance.constructor entity.name.type.instance.coffee" );
    }

    SECTION("only return matched capture groups when match rule has an optional captre groups") {
        string data = load_string("fixture/coffee-script.json");
        string source = "class Foo";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 4 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == source );
        REQUIRE( tokens[1].second.name() == "source.coffee meta.class.coffee" );

        REQUIRE( tokens[2].first.substr(source) == "class" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.class.coffee storage.type.class.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "Foo" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.class.coffee entity.name.type.class.coffee" );
    }

    /* The tokens are sorted in the way similar to the in-order traversal of the AST
     * This holds true even for begin_captures/end_capture/captures rules, because
     * capture group themselves have hierarchy. Although capture rules are listed in
     * any order, since we simply apply scope to the captured group in ascending order(0,1...N),
     * we are actually apply in the same way of the in-order traversal.
     *
     * e.g. ((()())()), the capture group number are corresponding to below tree
     *             1
     *           /   \
     *          2     5
     *         / \
     *        3   4
     */
    SECTION("when encounter nested capture, ensure parent capture group appears ahead of child") {
        string data = load_string("fixture/coffee-script.json");
        string source = "  destroy: ->";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 6 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == "destroy" );
        REQUIRE( tokens[1].second.name() == "source.coffee meta.function.coffee" );

        REQUIRE( tokens[2].first.substr(source) == "destroy" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "y" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee entity.name.function.coffee" );

        REQUIRE( tokens[4].first.substr(source) == ":" );
        REQUIRE( tokens[4].second.name() == "source.coffee keyword.operator.coffee" );

        REQUIRE( tokens[5].first.substr(source) == "->" );
        REQUIRE( tokens[5].second.name() == "source.coffee storage.type.function.coffee" );
    }

    SECTION("when capture beyond the matched range (i.e. capture in a look ahead/behind group)") {
        string data = load_string("fixture/coffee-script.json");
        string source = "  destroy: ->";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 6 );

        // the scope "source.coffee meta.function.coffee storage.type.function.coffee" captures
        // "->", which is beyond the matching range. so ignore it.
        REQUIRE_FALSE( tokens[4].first.substr(source) == "->" );
        REQUIRE_FALSE( tokens[4].second.name() == "source.coffee meta.function.coffee storage.type.function.coffee" );
    }

    SECTION("the enclosed/nested capture group will has additional ScopeNames") {
        string data = load_string("fixture/coffee-script.json");
        string source = "  destroy: ->";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 6 );

        REQUIRE( tokens[2].first.substr(source) == "destroy" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "y" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee entity.name.function.coffee" );

    }

    SECTION("the include rule wil do its work") {
        string data = load_string("fixture/coffee-script.json");
        string source = "1233456";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 2 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == "1233456" );
        REQUIRE( tokens[1].second.name() == "source.coffee constant.numeric.coffee" );
    }
}
<commit_msg>add test for interpolated string<commit_after>#include "catch.hpp"
#include "util.h"
#include <shl_exception.h>
#include <tokenizer.h>
#include <grammar_loader.h>

using namespace shl;

TEST_CASE("Tokenizer Tests") {
    Tokenizer tokenizer;
    GrammarLoader loader;

    SECTION("can load grammar and tokenizer string") {
        string data = load_string("fixture/hello.json");
        string source = "hello world!";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 4 );
        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.hello" );

        REQUIRE( tokens[1].first.substr(source) == "hello" );
        REQUIRE( tokens[1].second.name() == "source.hello prefix.hello" );

        REQUIRE( tokens[2].first.substr(source) == "world!" );
        REQUIRE( tokens[2].second.name() == "source.hello suffix.hello" );

        REQUIRE( tokens[3].first.substr(source) == "!" );
        REQUIRE( tokens[3].second.name() == "source.hello suffix.hello emphasis.hello" );
    }

    SECTION("return a single token when matches a single pattern with no capture groups") {
        string data = load_string("fixture/coffee-script.json");
        string source = "return";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 2 );
        REQUIRE( tokens[1].first.substr(source) == "return" );
        REQUIRE( tokens[1].second.name() == "source.coffee keyword.control.coffee" );
    }

    SECTION("return several tokens when matches a single pattern with capture gorups") {
        string data = load_string("fixture/coffee-script.json");
        string source = "new foo.bar.Baz";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 4 );

        REQUIRE( tokens[1].first.substr(source) == source );
        REQUIRE( tokens[1].second.name() == "source.coffee meta.class.instance.constructor" );

        REQUIRE( tokens[2].first.substr(source) == "new" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.class.instance.constructor keyword.operator.new.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "foo.bar.Baz" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.class.instance.constructor entity.name.type.instance.coffee" );
    }

    SECTION("return grammar top level token when no match at all") {
        string data = load_string("fixture/text.json");
        string source = "   ";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 1 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "text.plain" );
    }

    SECTION("will throw when scope is not properly closed (i.e. source code is malformed)") {
        string data = load_string("fixture/c.json");
        string source = "int main() {";
        Grammar g = loader.load(data);

        REQUIRE_THROWS_AS(tokenizer.tokenize(g, source), InvalidSourceException);
    }

    SECTION("will not throw when scope is not properly closed, if OPTION_TOLERATE_ERROR is specified") {
        string data = load_string("fixture/c.json");
        string source = "int main() {";
        Grammar g = loader.load(data);
        Tokenizer tokenizer(Tokenizer::OPTION_TOLERATE_ERROR);

        REQUIRE_NOTHROW( tokenizer.tokenize(g, source) );
    }

    SECTION("the enclosing scope will cover the sub-scopes") {
        string data = load_string("fixture/coffee-script.json");
        string source = " return new foo.bar.Baz ";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 5 );
        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == "return" );
        REQUIRE( tokens[1].second.name() == "source.coffee keyword.control.coffee" );

        REQUIRE( tokens[2].first.substr(source) == "new foo.bar.Baz" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.class.instance.constructor" );

        REQUIRE( tokens[3].first.substr(source) == "new" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.class.instance.constructor keyword.operator.new.coffee" );

        REQUIRE( tokens[4].first.substr(source) == "foo.bar.Baz" );
        REQUIRE( tokens[4].second.name() == "source.coffee meta.class.instance.constructor entity.name.type.instance.coffee" );
    }

    SECTION("only return matched capture groups when match rule has an optional captre groups") {
        string data = load_string("fixture/coffee-script.json");
        string source = "class Foo";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 4 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == source );
        REQUIRE( tokens[1].second.name() == "source.coffee meta.class.coffee" );

        REQUIRE( tokens[2].first.substr(source) == "class" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.class.coffee storage.type.class.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "Foo" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.class.coffee entity.name.type.class.coffee" );
    }

    /* The tokens are sorted in the way similar to the in-order traversal of the AST
     * This holds true even for begin_captures/end_capture/captures rules, because
     * capture group themselves have hierarchy. Although capture rules are listed in
     * any order, since we simply apply scope to the captured group in ascending order(0,1...N),
     * we are actually apply in the same way of the in-order traversal.
     *
     * e.g. ((()())()), the capture group number are corresponding to below tree
     *             1
     *           /   \
     *          2     5
     *         / \
     *        3   4
     */
    SECTION("when encounter nested capture, ensure parent capture group appears ahead of child") {
        string data = load_string("fixture/coffee-script.json");
        string source = "  destroy: ->";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 6 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == "destroy" );
        REQUIRE( tokens[1].second.name() == "source.coffee meta.function.coffee" );

        REQUIRE( tokens[2].first.substr(source) == "destroy" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "y" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee entity.name.function.coffee" );

        REQUIRE( tokens[4].first.substr(source) == ":" );
        REQUIRE( tokens[4].second.name() == "source.coffee keyword.operator.coffee" );

        REQUIRE( tokens[5].first.substr(source) == "->" );
        REQUIRE( tokens[5].second.name() == "source.coffee storage.type.function.coffee" );
    }

    SECTION("when capture beyond the matched range (i.e. capture in a look ahead/behind group)") {
        string data = load_string("fixture/coffee-script.json");
        string source = "  destroy: ->";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 6 );

        // the scope "source.coffee meta.function.coffee storage.type.function.coffee" captures
        // "->", which is beyond the matching range. so ignore it.
        REQUIRE_FALSE( tokens[4].first.substr(source) == "->" );
        REQUIRE_FALSE( tokens[4].second.name() == "source.coffee meta.function.coffee storage.type.function.coffee" );
    }

    SECTION("the enclosed/nested capture group will has additional ScopeNames") {
        string data = load_string("fixture/coffee-script.json");
        string source = "  destroy: ->";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 6 );

        REQUIRE( tokens[2].first.substr(source) == "destroy" );
        REQUIRE( tokens[2].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "y" );
        REQUIRE( tokens[3].second.name() == "source.coffee meta.function.coffee entity.name.function.coffee entity.name.function.coffee" );

    }

    SECTION("the include rule wil do its work") {
        string data = load_string("fixture/coffee-script.json");
        string source = "1233456";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 2 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == "1233456" );
        REQUIRE( tokens[1].second.name() == "source.coffee constant.numeric.coffee" );
    }

    SECTION("can handle interpolated string") {
        string data = load_string("fixture/coffee-script.json");
        string source = "\"the value is #{@x} my friend\"";
        Grammar g = loader.load(data);
        auto tokens = tokenizer.tokenize(g, source);

        REQUIRE( tokens.size() == 7 );

        REQUIRE( tokens[0].first.substr(source) == source );
        REQUIRE( tokens[0].second.name() == "source.coffee" );

        REQUIRE( tokens[1].first.substr(source) == source );
        REQUIRE( tokens[1].second.name() == "source.coffee string.quoted.double.coffee" );

        REQUIRE( tokens[2].first.substr(source) == "\"" );
        REQUIRE( tokens[2].second.name() == "source.coffee string.quoted.double.coffee punctuation.definition.string.begin.coffee" );

        REQUIRE( tokens[3].first.substr(source) == "#{@x}" );
        REQUIRE( tokens[3].second.name() == "source.coffee string.quoted.double.coffee source.coffee.embedded.source" );

        REQUIRE( tokens[4].first.substr(source) == "#{" );
        REQUIRE( tokens[4].second.name() == "source.coffee string.quoted.double.coffee source.coffee.embedded.source punctuation.section.embedded.coffee" );

        REQUIRE( tokens[5].first.substr(source) == "}" );
        REQUIRE( tokens[5].second.name() == "source.coffee string.quoted.double.coffee source.coffee.embedded.source punctuation.section.embedded.coffee" );

        REQUIRE( tokens[6].first.substr(source) == "\"" );
        REQUIRE( tokens[6].second.name() == "source.coffee string.quoted.double.coffee punctuation.definition.string.end.coffee" );
    }
}
<|endoftext|>


<commit_before>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// Licensed under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License. You
// may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

#include "bitmap/aa-line.hh"
#include "bitmap/auto-crop.hh"
#include "bitmap/bitmap.hh"
#include "bitmap/bitmap-exception.hh"
#include "bitmap/color.hh"
#include "bitmap/draw.hh"
#include "bitmap/filter.hh"
#include "bitmap/gaussian-blur.hh"
#include "bitmap/quantize.hh"
#include "geo/axis.hh"
#include "text/text-expression-context.hh"
#include "util/at-most.hh"
#include "rendering/faint-dc.hh"
#include "util/command-util.hh" // Fixme: Remove
#include "util/optional.hh"
#include "objects/object.hh"
#include "python/py-common.hh"
#include "python/mapped-type.hh"
#include "python/py-include.hh"
#include "python/py-add-type-object.hh"
#include "python/py-bitmap.hh"
#include "python/py-tri.hh"
#include "python/py-util.hh"
#include "python/py-ugly-forward.hh"

namespace faint{

template<typename FUNC>
auto bmp_exception_to_py(FUNC&& func){
  try{
    return func();
  }
  catch (const BitmapOutOfMemory&){
    throw MemoryError("Failed allocating memory for Bitmap");
  }
  catch (const BitmapStrideError&){
    throw MemoryError("Failed initializing bitmap stride (width too large?)");
  }
  catch (const BitmapException& e){
    throw MemoryError(space_sep("Bitmap error:", e.what()));
  }
}

template<>
struct MappedType<Bitmap&>{
  using PYTHON_TYPE = bitmapObject;

  static Bitmap& GetCppObject(bitmapObject* self){
    return self->bmp;
  }

  static bool Expired(bitmapObject* self){
    return !bitmap_ok(self->bmp);
  }

  static void ShowError(bitmapObject*){
    PyErr_SetString(PyExc_ValueError, "Operation attempted on bad bitmap.");
  }

  static utf8_string DefaultRepr(const bitmapObject*){
    return "Invalid Bitmap";
  }
};

static void Bitmap_init(bitmapObject& self,
  const IntSize& size,
  const Optional<Paint>& bg)
{
  bmp_exception_to_py([&](){
    if (size.w <= 0 || size.h <= 0){
      throw ValueError("Negative size");
    }
    self.bmp = Bitmap(size, bg.Or(Paint(color_white)));
  });
}

static utf8_string Bitmap_repr(Bitmap&){
  return "Bitmap";
}

static void Bitmap_dealloc(bitmapObject* self){
  self->ob_base.ob_type->tp_free((PyObject*)self);
}

/* method: "__copy__()->bmp\n
Used by Python copy.copy"
name: "__copy__" */
static Bitmap Bitmap_copy(Bitmap&);

PyObject* Bitmap_richcompare(bitmapObject* self, PyObject* otherRaw, int op){
  if (!PyObject_IsInstance(otherRaw, (PyObject*)&BitmapType)){
    Py_RETURN_NOTIMPLEMENTED;
  }
  if (op != Py_EQ){
    Py_RETURN_NOTIMPLEMENTED;
  }

  auto other((bitmapObject*)otherRaw);
  const Bitmap& lhs(self->bmp);
  const Bitmap& rhs(other->bmp);
  if (lhs == rhs){
    Py_RETURN_TRUE;
  }
  Py_RETURN_FALSE;
}

/* method: "copy()->bmp\n
Returns a copy of the bitmap."
name: "copy" */
static Bitmap Bitmap_copy(Bitmap& self){
  return self;
}

/* method: "draw_objects(objects)\n
Draw the objects onto this bitmap." */
static void Bitmap_draw_objects(Bitmap& self, const objects_t& objects){
  class NoCtx : public ExpressionContext{
    Optional<Calibration> GetCalibration() const override{
      return {};
    }

    const Object* GetObject(const utf8_string&) const override{
      return nullptr;
    }
  };

  FaintDC dc(self);
  NoCtx ctx;
  for (Object* obj : objects){
    obj->Draw(dc, ctx);
  }
}

/* method: "get_raw_rgb_string()->s\n
Returns the bitmap as a bytes object with binary rgb values." */
static std::string Bitmap_get_raw_rgb_string(Bitmap& self){
  std::string str;
  str.reserve(to_size_t(area(self.GetSize())*3));
  for (int y = 0; y != self.m_h; y++){
    for (int x = 0; x != self.m_w; x++){
      Color c(get_color_raw(self, x, y));
      str += static_cast<char>(c.r);
      str += static_cast<char>(c.g);
      str += static_cast<char>(c.b);
    }
  }
  return str;
}

/* method: "subbitmap((x,y,w,h))->Bitmap\n
Returns the bitmap inside the specified rectangle." */
static Bitmap Bitmap_subbitmap(Bitmap& self, const IntRect& r){
  if (!fully_inside(r, self)){
    throw ValueError("Rectangle extends outside bitmap.");
  }
  if (empty(r)){
    throw ValueError("Empty rectangle.");
  }
  return subbitmap(self, r);
}

/* method: "get_size()->w,h\n
Returns the width and height of the bitmap." */
static IntSize Bitmap_get_size(Bitmap& self){
  return self.GetSize();
}

/* method: "set_pixel((x,y),(r,g,b[,a]))\n
Set the pixel at (x,y) to the specified color." */
static void Bitmap_set_pixel(Bitmap& self, const IntPoint& pos, const Color& c){
  if (invalid_pixel_pos(pos, self)){
    throw PresetFunctionError();
  }
  put_pixel(self, pos, c);
}

/* method: "get_pixel(x,y) -> (r,g,b,a)\n
Get the color at (x,y) as an RGBA-tuple." */
static Color Bitmap_get_pixel(Bitmap& self, const IntPoint& pos){
  if (invalid_pixel_pos(pos, self)){
    throw PresetFunctionError();
  }
  return get_color(self, pos);
}

/* method: "line(x0,y0,x1,y1)\n
Draw a line from x0, y0 to x1, y1" */
static void Bitmap_line(Bitmap& self, const IntLineSegment& line, const Color& c){
  draw_line(self, line, solid_1px(c));
}

static PyObject* Bitmap_new(PyTypeObject* type, PyObject*, PyObject*){
  bitmapObject* self;
  self = (bitmapObject*)type->tp_alloc(type, 0);
  return (PyObject*)self;
}

using common_type = Bitmap&;

// Specializations since Bitmap doesn't support commands
template<>
void Common_aa_line<Bitmap&>(Bitmap& bmp, const IntLineSegment& line,
  const ColRGB& color)
{
  draw_line_aa_Wu(bmp, line, color);
}

template<>
bool Common_auto_crop(Bitmap& bmp){
  return get_auto_crop_rectangles(bmp).Visit(
  [](){
    // Do nothing if not auto-croppable
    return false;
  },
  [&bmp](const IntRect& r){
    bmp = subbitmap(bmp, r);
    return true;
  },
  [&bmp](const IntRect& r0, const IntRect&){
    bmp = subbitmap(bmp, r0);
    return true;
  });
}

template<>
void Common_blit(Bitmap& dst, const IntPoint& topLeft, const Bitmap& src){
  blit(offsat(src, topLeft), onto(dst));
}

template<>
void Common_boundary_fill(Bitmap& bmp, const IntPoint& pos, const Paint& fill,
  const Color& boundary)
{
  boundary_fill(bmp, pos, fill, boundary);
}

template<>
void Common_clear(Bitmap& bmp, const Paint& paint){
  clear(bmp, paint);
}

template<>
void Common_color_balance(Bitmap& bmp, const color_range_t& r,
  const color_range_t& g,
  const color_range_t& b)
{
  color_balance(bmp, r, g, b);
}

template<>
int Common_color_count(Bitmap& bmp){
  return count_colors(bmp);
}

template<>
void Common_desaturate(Bitmap& bmp){
  desaturate_simple(bmp);
}

template<>
void Common_desaturate_weighted(Bitmap& bmp){
  desaturate_weighted(bmp);
}

template<>
void Common_erase_but_color(Bitmap& bmp, const Color& keep,
  const Optional<Paint>& eraser)
{
  if (eraser.NotSet()){
    throw ValueError("Erase color not specified.");
  }
  if (keep == eraser.Get()){
    throw ValueError("Same erase color as the kept color");
  }
  erase_but(bmp, keep, eraser.Get());
}

template<>
void Common_flip_horizontally(Bitmap& bmp){
  flip(bmp, along(Axis::HORIZONTAL));
}

template<>
void Common_flip_vertically(Bitmap& bmp){
  flip(bmp, along(Axis::VERTICAL));
}

template<>
void Common_fill(Bitmap& bmp, const IntPoint& pos, const Paint& paint){
  if (!point_in_bitmap(bmp, pos)){
    throw ValueError("Fill origin outside Bitmap");
  }
  flood_fill(bmp, pos, paint);
}

template<>
void Common_gaussian_blur(Bitmap& bmp, coord sigma){
  gaussian_blur_fast(bmp, sigma);
}

template<>
void Common_invert(Bitmap& bmp){
  invert(bmp);
}

template<>
void Common_apply_paste(Bitmap& dst, const IntPoint& pos, const Bitmap& src){
  blit(offsat(src, pos), onto(dst));
}

template<>
void Common_pixelize(Bitmap& bmp, const pixelize_range_t& width){
  pixelize(bmp, width);
}

template<>
void Common_quantize(Bitmap& bmp){
  quantize(bmp, Dithering::ON);
}

template<>
void Common_replace_alpha(Bitmap& bmp, const ColRGB& color){
  blend_alpha(bmp, color);
}

template<>
void Common_replace_color(Bitmap& bmp, const Color& old,
  const Paint& replacement)
{
  replace_color(bmp, Old(old), replacement);
}

template<>
void Common_rotate(Bitmap& bmp, const Angle& angle, const Optional<Paint>& bg){
  if (bg.NotSet()){
    throw ValueError("No background specified!");
  }
  bmp = rotate_bilinear(bmp, angle, bg.Get());
}

template<>
void Common_sepia(Bitmap& bmp, int intensity){
  sepia(bmp, intensity);
}

template<>
void Common_set_alpha(Bitmap& bmp, const color_value_t& alpha){
  set_alpha(bmp, static_cast<uchar>(alpha.GetValue()));
}

template<>
void Common_set_threshold(Bitmap& bmp, const std::pair<double, double>& range,
  const Optional<Paint>& in, const Optional<Paint>& out)
{
  if (in.NotSet()){
    throw ValueError("No inside fill specified");
  }
  if (out.NotSet()){
    throw ValueError("No outside fill specified");
  }
  const auto lower = constrained(Min(0.0), range.first, Max(1.0));
  const auto upper = constrained(Min(0.0), range.second, Max(1.0));
  const auto r = fractional_bounded_interval<threshold_range_t>(lower, upper);
  threshold(bmp, r, in.Get(), out.Get());
}

#define COMMONFWD(bundle)FORWARDER(bundle::Func<Bitmap&>, bundle::ArgType(), bundle::Name(), bundle::Doc())

/* extra_include: "generated/python/method-def/py-common-method-def.hh" */

/* property: "Bitmap size" */
struct bitmap_size{
  static IntSize Get(Bitmap& self){
    return self.GetSize();
  }

  static void Set(Bitmap& self, const IntSize& size){
    bmp_exception_to_py(
      [&](){
        Bitmap temp(size, color_white);
        blit(at_top_left(self), onto(temp));
        self = temp;
      });
  }
};

#include "generated/python/method-def/py-bitmap-method-def.hh"

PyTypeObject BitmapType = {
  PyVarObject_HEAD_INIT(nullptr, 0)
  "Bitmap", // tp_name
  sizeof(bitmapObject), // tp_basicsize
  0, // tp_itemsize
  (destructor)Bitmap_dealloc, // tp_dealloc
  nullptr, // tp_print
  nullptr, // tp_getattr
  nullptr, // tp_setattr
  nullptr, // tp_compare
  REPR_FORWARDER(Bitmap_repr), // tp_repr
  nullptr, // tp_as_number
  nullptr, // tp_as_sequence
  nullptr, // tp_as_mapping
  nullptr, // tp_hash
  nullptr, // tp_call
  nullptr, // tp_str
  nullptr, // tp_getattro
  nullptr, // tp_setattro
  nullptr, // tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
  // tp_doc
  "Bitmap for RGBA pixel data.",
  nullptr, // tp_traverse
  nullptr, // tp_clear
  (richcmpfunc)Bitmap_richcompare, // tp_richcompare
  0, // tp_weaklistoffset
  nullptr, // tp_iter
  nullptr, // tp_iternext
  bitmap_methods, // tp_methods
  nullptr, // tp_members
  bitmap_getseters, // tp_getset
  nullptr, // tp_base
  nullptr, // tp_dict
  nullptr, // tp_descr_get
  nullptr, // tp_descr_set
  0, // tp_dictoffset
  INIT_FORWARDER(Bitmap_init), // tp_init
  nullptr, // tp_alloc
  Bitmap_new, // tp_new
  nullptr, // tp_free
  nullptr, // tp_is_gc
  nullptr, // tp_bases
  nullptr, // tp_mro
  nullptr, // tp_cache
  nullptr, // tp_subclasses
  nullptr, // tp_weaklist
  nullptr, // tp_del
  0, // tp_version_tag
  nullptr // tp_finalize
};

void add_type_Bitmap(PyObject* module){
  add_type_object(module, BitmapType, "Bitmap");
}

} // namespace
<commit_msg>Removed unused include.<commit_after>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// Licensed under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License. You
// may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

#include "bitmap/aa-line.hh"
#include "bitmap/auto-crop.hh"
#include "bitmap/bitmap.hh"
#include "bitmap/bitmap-exception.hh"
#include "bitmap/color.hh"
#include "bitmap/draw.hh"
#include "bitmap/filter.hh"
#include "bitmap/gaussian-blur.hh"
#include "bitmap/quantize.hh"
#include "geo/axis.hh"
#include "text/text-expression-context.hh"
#include "util/at-most.hh"
#include "rendering/faint-dc.hh"
#include "util/optional.hh"
#include "objects/object.hh"
#include "python/py-common.hh"
#include "python/mapped-type.hh"
#include "python/py-include.hh"
#include "python/py-add-type-object.hh"
#include "python/py-bitmap.hh"
#include "python/py-tri.hh"
#include "python/py-util.hh"
#include "python/py-ugly-forward.hh"

namespace faint{

template<typename FUNC>
auto bmp_exception_to_py(FUNC&& func){
  try{
    return func();
  }
  catch (const BitmapOutOfMemory&){
    throw MemoryError("Failed allocating memory for Bitmap");
  }
  catch (const BitmapStrideError&){
    throw MemoryError("Failed initializing bitmap stride (width too large?)");
  }
  catch (const BitmapException& e){
    throw MemoryError(space_sep("Bitmap error:", e.what()));
  }
}

template<>
struct MappedType<Bitmap&>{
  using PYTHON_TYPE = bitmapObject;

  static Bitmap& GetCppObject(bitmapObject* self){
    return self->bmp;
  }

  static bool Expired(bitmapObject* self){
    return !bitmap_ok(self->bmp);
  }

  static void ShowError(bitmapObject*){
    PyErr_SetString(PyExc_ValueError, "Operation attempted on bad bitmap.");
  }

  static utf8_string DefaultRepr(const bitmapObject*){
    return "Invalid Bitmap";
  }
};

static void Bitmap_init(bitmapObject& self,
  const IntSize& size,
  const Optional<Paint>& bg)
{
  bmp_exception_to_py([&](){
    if (size.w <= 0 || size.h <= 0){
      throw ValueError("Negative size");
    }
    self.bmp = Bitmap(size, bg.Or(Paint(color_white)));
  });
}

static utf8_string Bitmap_repr(Bitmap&){
  return "Bitmap";
}

static void Bitmap_dealloc(bitmapObject* self){
  self->ob_base.ob_type->tp_free((PyObject*)self);
}

/* method: "__copy__()->bmp\n
Used by Python copy.copy"
name: "__copy__" */
static Bitmap Bitmap_copy(Bitmap&);

PyObject* Bitmap_richcompare(bitmapObject* self, PyObject* otherRaw, int op){
  if (!PyObject_IsInstance(otherRaw, (PyObject*)&BitmapType)){
    Py_RETURN_NOTIMPLEMENTED;
  }
  if (op != Py_EQ){
    Py_RETURN_NOTIMPLEMENTED;
  }

  auto other((bitmapObject*)otherRaw);
  const Bitmap& lhs(self->bmp);
  const Bitmap& rhs(other->bmp);
  if (lhs == rhs){
    Py_RETURN_TRUE;
  }
  Py_RETURN_FALSE;
}

/* method: "copy()->bmp\n
Returns a copy of the bitmap."
name: "copy" */
static Bitmap Bitmap_copy(Bitmap& self){
  return self;
}

/* method: "draw_objects(objects)\n
Draw the objects onto this bitmap." */
static void Bitmap_draw_objects(Bitmap& self, const objects_t& objects){
  class NoCtx : public ExpressionContext{
    Optional<Calibration> GetCalibration() const override{
      return {};
    }

    const Object* GetObject(const utf8_string&) const override{
      return nullptr;
    }
  };

  FaintDC dc(self);
  NoCtx ctx;
  for (Object* obj : objects){
    obj->Draw(dc, ctx);
  }
}

/* method: "get_raw_rgb_string()->s\n
Returns the bitmap as a bytes object with binary rgb values." */
static std::string Bitmap_get_raw_rgb_string(Bitmap& self){
  std::string str;
  str.reserve(to_size_t(area(self.GetSize())*3));
  for (int y = 0; y != self.m_h; y++){
    for (int x = 0; x != self.m_w; x++){
      Color c(get_color_raw(self, x, y));
      str += static_cast<char>(c.r);
      str += static_cast<char>(c.g);
      str += static_cast<char>(c.b);
    }
  }
  return str;
}

/* method: "subbitmap((x,y,w,h))->Bitmap\n
Returns the bitmap inside the specified rectangle." */
static Bitmap Bitmap_subbitmap(Bitmap& self, const IntRect& r){
  if (!fully_inside(r, self)){
    throw ValueError("Rectangle extends outside bitmap.");
  }
  if (empty(r)){
    throw ValueError("Empty rectangle.");
  }
  return subbitmap(self, r);
}

/* method: "get_size()->w,h\n
Returns the width and height of the bitmap." */
static IntSize Bitmap_get_size(Bitmap& self){
  return self.GetSize();
}

/* method: "set_pixel((x,y),(r,g,b[,a]))\n
Set the pixel at (x,y) to the specified color." */
static void Bitmap_set_pixel(Bitmap& self, const IntPoint& pos, const Color& c){
  if (invalid_pixel_pos(pos, self)){
    throw PresetFunctionError();
  }
  put_pixel(self, pos, c);
}

/* method: "get_pixel(x,y) -> (r,g,b,a)\n
Get the color at (x,y) as an RGBA-tuple." */
static Color Bitmap_get_pixel(Bitmap& self, const IntPoint& pos){
  if (invalid_pixel_pos(pos, self)){
    throw PresetFunctionError();
  }
  return get_color(self, pos);
}

/* method: "line(x0,y0,x1,y1)\n
Draw a line from x0, y0 to x1, y1" */
static void Bitmap_line(Bitmap& self, const IntLineSegment& line, const Color& c){
  draw_line(self, line, solid_1px(c));
}

static PyObject* Bitmap_new(PyTypeObject* type, PyObject*, PyObject*){
  bitmapObject* self;
  self = (bitmapObject*)type->tp_alloc(type, 0);
  return (PyObject*)self;
}

using common_type = Bitmap&;

// Specializations since Bitmap doesn't support commands
template<>
void Common_aa_line<Bitmap&>(Bitmap& bmp, const IntLineSegment& line,
  const ColRGB& color)
{
  draw_line_aa_Wu(bmp, line, color);
}

template<>
bool Common_auto_crop(Bitmap& bmp){
  return get_auto_crop_rectangles(bmp).Visit(
  [](){
    // Do nothing if not auto-croppable
    return false;
  },
  [&bmp](const IntRect& r){
    bmp = subbitmap(bmp, r);
    return true;
  },
  [&bmp](const IntRect& r0, const IntRect&){
    bmp = subbitmap(bmp, r0);
    return true;
  });
}

template<>
void Common_blit(Bitmap& dst, const IntPoint& topLeft, const Bitmap& src){
  blit(offsat(src, topLeft), onto(dst));
}

template<>
void Common_boundary_fill(Bitmap& bmp, const IntPoint& pos, const Paint& fill,
  const Color& boundary)
{
  boundary_fill(bmp, pos, fill, boundary);
}

template<>
void Common_clear(Bitmap& bmp, const Paint& paint){
  clear(bmp, paint);
}

template<>
void Common_color_balance(Bitmap& bmp, const color_range_t& r,
  const color_range_t& g,
  const color_range_t& b)
{
  color_balance(bmp, r, g, b);
}

template<>
int Common_color_count(Bitmap& bmp){
  return count_colors(bmp);
}

template<>
void Common_desaturate(Bitmap& bmp){
  desaturate_simple(bmp);
}

template<>
void Common_desaturate_weighted(Bitmap& bmp){
  desaturate_weighted(bmp);
}

template<>
void Common_erase_but_color(Bitmap& bmp, const Color& keep,
  const Optional<Paint>& eraser)
{
  if (eraser.NotSet()){
    throw ValueError("Erase color not specified.");
  }
  if (keep == eraser.Get()){
    throw ValueError("Same erase color as the kept color");
  }
  erase_but(bmp, keep, eraser.Get());
}

template<>
void Common_flip_horizontally(Bitmap& bmp){
  flip(bmp, along(Axis::HORIZONTAL));
}

template<>
void Common_flip_vertically(Bitmap& bmp){
  flip(bmp, along(Axis::VERTICAL));
}

template<>
void Common_fill(Bitmap& bmp, const IntPoint& pos, const Paint& paint){
  if (!point_in_bitmap(bmp, pos)){
    throw ValueError("Fill origin outside Bitmap");
  }
  flood_fill(bmp, pos, paint);
}

template<>
void Common_gaussian_blur(Bitmap& bmp, coord sigma){
  gaussian_blur_fast(bmp, sigma);
}

template<>
void Common_invert(Bitmap& bmp){
  invert(bmp);
}

template<>
void Common_apply_paste(Bitmap& dst, const IntPoint& pos, const Bitmap& src){
  blit(offsat(src, pos), onto(dst));
}

template<>
void Common_pixelize(Bitmap& bmp, const pixelize_range_t& width){
  pixelize(bmp, width);
}

template<>
void Common_quantize(Bitmap& bmp){
  quantize(bmp, Dithering::ON);
}

template<>
void Common_replace_alpha(Bitmap& bmp, const ColRGB& color){
  blend_alpha(bmp, color);
}

template<>
void Common_replace_color(Bitmap& bmp, const Color& old,
  const Paint& replacement)
{
  replace_color(bmp, Old(old), replacement);
}

template<>
void Common_rotate(Bitmap& bmp, const Angle& angle, const Optional<Paint>& bg){
  if (bg.NotSet()){
    throw ValueError("No background specified!");
  }
  bmp = rotate_bilinear(bmp, angle, bg.Get());
}

template<>
void Common_sepia(Bitmap& bmp, int intensity){
  sepia(bmp, intensity);
}

template<>
void Common_set_alpha(Bitmap& bmp, const color_value_t& alpha){
  set_alpha(bmp, static_cast<uchar>(alpha.GetValue()));
}

template<>
void Common_set_threshold(Bitmap& bmp, const std::pair<double, double>& range,
  const Optional<Paint>& in, const Optional<Paint>& out)
{
  if (in.NotSet()){
    throw ValueError("No inside fill specified");
  }
  if (out.NotSet()){
    throw ValueError("No outside fill specified");
  }
  const auto lower = constrained(Min(0.0), range.first, Max(1.0));
  const auto upper = constrained(Min(0.0), range.second, Max(1.0));
  const auto r = fractional_bounded_interval<threshold_range_t>(lower, upper);
  threshold(bmp, r, in.Get(), out.Get());
}

#define COMMONFWD(bundle)FORWARDER(bundle::Func<Bitmap&>, bundle::ArgType(), bundle::Name(), bundle::Doc())

/* extra_include: "generated/python/method-def/py-common-method-def.hh" */

/* property: "Bitmap size" */
struct bitmap_size{
  static IntSize Get(Bitmap& self){
    return self.GetSize();
  }

  static void Set(Bitmap& self, const IntSize& size){
    bmp_exception_to_py(
      [&](){
        Bitmap temp(size, color_white);
        blit(at_top_left(self), onto(temp));
        self = temp;
      });
  }
};

#include "generated/python/method-def/py-bitmap-method-def.hh"

PyTypeObject BitmapType = {
  PyVarObject_HEAD_INIT(nullptr, 0)
  "Bitmap", // tp_name
  sizeof(bitmapObject), // tp_basicsize
  0, // tp_itemsize
  (destructor)Bitmap_dealloc, // tp_dealloc
  nullptr, // tp_print
  nullptr, // tp_getattr
  nullptr, // tp_setattr
  nullptr, // tp_compare
  REPR_FORWARDER(Bitmap_repr), // tp_repr
  nullptr, // tp_as_number
  nullptr, // tp_as_sequence
  nullptr, // tp_as_mapping
  nullptr, // tp_hash
  nullptr, // tp_call
  nullptr, // tp_str
  nullptr, // tp_getattro
  nullptr, // tp_setattro
  nullptr, // tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
  // tp_doc
  "Bitmap for RGBA pixel data.",
  nullptr, // tp_traverse
  nullptr, // tp_clear
  (richcmpfunc)Bitmap_richcompare, // tp_richcompare
  0, // tp_weaklistoffset
  nullptr, // tp_iter
  nullptr, // tp_iternext
  bitmap_methods, // tp_methods
  nullptr, // tp_members
  bitmap_getseters, // tp_getset
  nullptr, // tp_base
  nullptr, // tp_dict
  nullptr, // tp_descr_get
  nullptr, // tp_descr_set
  0, // tp_dictoffset
  INIT_FORWARDER(Bitmap_init), // tp_init
  nullptr, // tp_alloc
  Bitmap_new, // tp_new
  nullptr, // tp_free
  nullptr, // tp_is_gc
  nullptr, // tp_bases
  nullptr, // tp_mro
  nullptr, // tp_cache
  nullptr, // tp_subclasses
  nullptr, // tp_weaklist
  nullptr, // tp_del
  0, // tp_version_tag
  nullptr // tp_finalize
};

void add_type_Bitmap(PyObject* module){
  add_type_object(module, BitmapType, "Bitmap");
}

} // namespace
<|endoftext|>


<commit_before>#include "QCodeEditor.h"
#include "xo/system/system_tools.h"
#include "xo/system/assert.h"
#include "xo/string/string_tools.h"
#include <QTextStream>
#include <QMessageBox>
#include <QFileDialog>

QCodeEditor::QCodeEditor( QWidget* parent ) :
QWidget( parent ),
textChangedFlag( false )
{
	QVBoxLayout* verticalLayout = new QVBoxLayout( this );
	verticalLayout->setContentsMargins( 0, 0, 0, 0 );
	setLayout( verticalLayout );
	textEdit = new QCodeTextEdit( this );

	QFont font;
	font.setFamily( QStringLiteral( "Consolas" ) );
	font.setPointSize( 9 );
	textEdit->setFont( font );
	textEdit->setLineWrapMode( QPlainTextEdit::NoWrap );
	textEdit->setTabStopWidth( 16 );
	textEdit->setWordWrapMode( QTextOption::NoWrap );
	verticalLayout->addWidget( textEdit );

	connect( textEdit, SIGNAL( textChanged() ), this, SLOT( textEditChanged() ) );
}

QCodeEditor::~QCodeEditor()
{}

QString QCodeEditor::getPlainText() const
{
	return textEdit->toPlainText();
}

void QCodeEditor::open( const QString& filename )
{
	QCodeSyntaxHighlighter* xmlSyntaxHighlighter = new QCodeSyntaxHighlighter( textEdit->document(), QCodeSyntaxHighlighter::detectLanguage( filename ) );

	QFile f( filename );
	if ( f.open( QFile::ReadOnly | QFile::Text ) )
	{
		QTextStream str( &f );
		QString data = str.readAll();
		textEdit->setPlainText( data );
		fileName = filename;
		textChangedFlag = false;
	}
	else xo_error( "Could not open file: " + filename.toStdString() );
}

void QCodeEditor::openDialog( const QString& folder, const QString& fileTypes )
{
	auto fn = QFileDialog::getOpenFileName( this, "Open File", folder, fileTypes );
	if ( !fn.isEmpty() )
		open( fn );
}

void QCodeEditor::save()
{
	QFile file( fileName );
	if ( !file.open( QIODevice::WriteOnly ) )
	{
		QMessageBox::critical( this, "Error writing file", "Could not open file " + fileName );
		return;
	}
	else
	{
		QTextStream stream( &file );
		stream << textEdit->toPlainText();
		stream.flush();
		file.close();
		textChangedFlag = false;
	}
}

void QCodeEditor::saveAs( const QString& fn )
{
	if ( getFileFormat( fn ) != getFileFormat( fileName ) )
	{
		std::stringstream stri( textEdit->toPlainText().toStdString() );
		xo::prop_node pn;
		stri >> xo::prop_node_deserializer( getFileFormat( fileName ), pn );
		std::stringstream stro;
		stro << xo::prop_node_serializer( getFileFormat( fn ), pn );

		QCodeSyntaxHighlighter* xmlSyntaxHighlighter = new QCodeSyntaxHighlighter( textEdit->document(), QCodeSyntaxHighlighter::detectLanguage( fn ) );
		textEdit->setPlainText( QString( stro.str().c_str() ) );
	}

	fileName = fn;
	save();
}

QString QCodeEditor::getTitle()
{
	return QFileInfo( fileName ).fileName() + ( hasTextChanged() ? "*" : "" );
}

void QCodeEditor::textEditChanged()
{
	if ( !textChangedFlag )
	{
		textChangedFlag = true;
		emit textChanged();
	}
}

xo::file_format QCodeEditor::getFileFormat( const QString& filename ) const
{
	auto ext = xo::path( filename.toStdString() ).extension();
	if ( ext == "xml" )
		return xo::file_format::xml;
	else if ( ext == "zml" )
		return xo::file_format::zml;
	else return xo::file_format::unknown;
}

//
// BasicXMLSyntaxHighlighter
//

QCodeSyntaxHighlighter::QCodeSyntaxHighlighter( QObject* parent, Language l ) : QSyntaxHighlighter( parent )
{
	setLanguage( l );
}

QCodeSyntaxHighlighter::QCodeSyntaxHighlighter( QTextDocument* parent, Language l ) : QSyntaxHighlighter( parent )
{
	setLanguage( l );
}

void QCodeSyntaxHighlighter::highlightBlock( const QString &text )
{
	if ( language == XML )
	{
		// Special treatment for xml element regex as we use captured text to emulate lookbehind
		int xmlElementIndex = m_xmlElementRegex.indexIn( text );
		while ( xmlElementIndex >= 0 )
		{
			int matchedPos = m_xmlElementRegex.pos( 1 );
			int matchedLength = m_xmlElementRegex.cap( 1 ).length();
			setFormat( matchedPos, matchedLength, m_ElementFormat );
			xmlElementIndex = m_xmlElementRegex.indexIn( text, matchedPos + matchedLength );
		}
		highlightByRegex( m_NumberFormat, m_NumberRegex, text );
		highlightByRegex( m_AttributeFormat, m_xmlAttributeRegex, text );
	}
	else
	{
		highlightByRegex( m_NumberFormat, m_NumberRegex, text );
		highlightByRegex( m_AttributeFormat, m_xmlAttributeRegex, text );
		highlightByRegex( m_ElementFormat, m_xmlElementRegex, text );
	}

	// Highlight xml keywords *after* xml elements to fix any occasional / captured into the enclosing element
	for ( auto& regex : m_xmlKeywordRegexes )
		highlightByRegex( m_KeywordFormat, regex, text );

	highlightByRegex( m_ValueFormat, m_xmlValueRegex, text );
	highlightByRegex( m_SpecialFormat, m_SpecialRegex, text );

	if ( language == ZML )
	{
		int i = m_xmlCommentRegex.indexIn( text );
		while  ( i >= 0 )
		{
			int quotes_before = 0;
			for ( int x = 0; x <= i; ++x )
				quotes_before += int( text[ x ] == '\"' );
			if ( quotes_before % 2 == 0 )
			{
				setFormat( i, m_xmlCommentRegex.matchedLength(), m_CommentFormat );
				break;
			}
			else i = m_xmlCommentRegex.indexIn( text, i + 1 );
		}
	}
	else
	{
		highlightByRegex( m_CommentFormat, m_xmlCommentRegex, text );
	}
}

void QCodeSyntaxHighlighter::highlightByRegex( const QTextCharFormat & format, const QRegExp & regex, const QString & text )
{
	int index = regex.indexIn( text );
	while ( index >= 0 )
	{
		int matchedLength = regex.matchedLength();
		setFormat( index, matchedLength, format );
		index = regex.indexIn( text, index + matchedLength );
	}
}

void QCodeSyntaxHighlighter::setRegexes()
{
	switch ( language )
	{
	case XML:
		m_xmlElementRegex.setPattern( "<[\\s]*[/]?[\\s]*([^\\n]\\w*)(?=[\\s/>])" );
		m_xmlAttributeRegex.setPattern( "\\w+(?=\\=)" );
		m_xmlValueRegex.setPattern( "\"[^\\n\"]+\"(?=[\\s/>])" );
		m_xmlCommentRegex.setPattern( "<!--[^\\n]*-->" );
		m_SpecialRegex.setPattern( "/.^/" );
		m_xmlKeywordRegexes = QList<QRegExp>() << QRegExp( "<\\?" ) << QRegExp( "/>" ) << QRegExp( ">" ) << QRegExp( "<" ) << QRegExp( "</" ) << QRegExp( "\\?>" );
		break;
	case ZML:
		m_xmlElementRegex.setPattern( "\\w+\\s*\\=\\s*\\{" );
		m_xmlAttributeRegex.setPattern( "\\w+\\s*(\\=)" );
		m_xmlValueRegex.setPattern( "\"[^\\n\"]+\"" );
		m_xmlCommentRegex.setPattern( ";[^\\n]*" );
		m_SpecialRegex.setPattern( "#\\w+" );
		m_xmlKeywordRegexes = QList<QRegExp>() << QRegExp( "\\{" ) << QRegExp( "\\}" ) << QRegExp( "\\[" ) << QRegExp( "\\]" ) << QRegExp( "\\=" );
		break;
	default:
		xo_error( "Unsupported language" );
	}

	m_NumberRegex.setPattern( "\\b([-+]?[\\.\\d]+)" );
}

void QCodeSyntaxHighlighter::setFormats()
{
	m_KeywordFormat.setForeground( Qt::darkGray );
	m_ElementFormat.setForeground( Qt::darkBlue );
	m_ElementFormat.setFontWeight( QFont::Bold );
	m_AttributeFormat.setForeground( Qt::darkBlue );
	//m_AttributeFormat.setFontWeight( QFont::Bold );
	m_ValueFormat.setForeground( Qt::darkRed );
	m_CommentFormat.setForeground( Qt::darkGreen );
	m_CommentFormat.setFontItalic( true );
	m_NumberFormat.setForeground( Qt::darkMagenta );
	m_SpecialFormat.setForeground( Qt::blue );
	m_SpecialFormat.setFontItalic( true );
	m_SpecialFormat.setFontWeight( QFont::Bold );
}

void QCodeSyntaxHighlighter::setLanguage( Language l )
{
	language = l;
	setRegexes();
	setFormats();
}

QCodeSyntaxHighlighter::Language QCodeSyntaxHighlighter::detectLanguage( const QString& filename )
{
	auto ext = xo::path( filename.toStdString() ).extension();
	if ( ext == "xml" )
		return XML;
	else if ( ext == "zml" )
		return ZML;
	else return XML;
}

//
// QCodeTextEdit
//

QCodeTextEdit::QCodeTextEdit( QWidget* parent ) : QPlainTextEdit( parent )
{
	lineNumberArea = new LineNumberArea( this );

	connect( this, SIGNAL( blockCountChanged( int ) ), this, SLOT( updateLineNumberAreaWidth( int ) ) );
	connect( this, SIGNAL( updateRequest( QRect, int ) ), this, SLOT( updateLineNumberArea( QRect, int ) ) );

	updateLineNumberAreaWidth( 0 );
}

void QCodeTextEdit::lineNumberAreaPaintEvent( QPaintEvent *event )
{
	QPainter painter( lineNumberArea );
	painter.fillRect( event->rect(), Qt::lightGray );

	QTextBlock block = firstVisibleBlock();
	int blockNumber = block.blockNumber();
	int top = (int)blockBoundingGeometry( block ).translated( contentOffset() ).top();
	int bottom = top + (int)blockBoundingRect( block ).height();

	while ( block.isValid() && top <= event->rect().bottom() ) {
		if ( block.isVisible() && bottom >= event->rect().top() ) {
			QString number = QString::number( blockNumber + 1 );
			painter.setPen( Qt::black );
			painter.drawText( 0, top, lineNumberArea->width() - 2, fontMetrics().height(),
				Qt::AlignRight, number );
		}

		block = block.next();
		top = bottom;
		bottom = top + (int)blockBoundingRect( block ).height();
		++blockNumber;
	}
}

int QCodeTextEdit::lineNumberAreaWidth()
{
	int digits = 1;
	int max = qMax( 1, blockCount() );
	while ( max >= 10 ) {
		max /= 10;
		++digits;
	}

	int space = 4 + fontMetrics().width( QLatin1Char( '9' ) ) * digits;

	return space;
}

void QCodeTextEdit::updateLineNumberAreaWidth( int newBlockCount )
{
	setViewportMargins( lineNumberAreaWidth(), 0, 0, 0 );
}

void QCodeTextEdit::updateLineNumberArea( const QRect& rect, int dy )
{
	if ( dy )
		lineNumberArea->scroll( 0, dy );
	else
		lineNumberArea->update( 0, rect.y(), lineNumberArea->width(), rect.height() );

	if ( rect.contains( viewport()->rect() ) )
		updateLineNumberAreaWidth( 0 );
}

void QCodeTextEdit::resizeEvent( QResizeEvent *event )
{
	QPlainTextEdit::resizeEvent( event );
	QRect cr = contentsRect();
	lineNumberArea->setGeometry( QRect( cr.left(), cr.top(), lineNumberAreaWidth(), cr.height() ) );
}

void QCodeTextEdit::keyPressEvent( QKeyEvent *e )
{
	if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
	{
		auto line = textCursor().block().text().toStdString();
		int tabs = 0;
		while ( tabs < line.size() && line[ tabs ] == '\t' )
			++tabs;
		QPlainTextEdit::keyPressEvent( e );
		QPlainTextEdit::insertPlainText( QString( tabs, '\t' ) );
	}
	else QPlainTextEdit::keyPressEvent( e );
}
<commit_msg>default highlighting is now ZML<commit_after>#include "QCodeEditor.h"
#include "xo/system/system_tools.h"
#include "xo/system/assert.h"
#include "xo/string/string_tools.h"
#include <QTextStream>
#include <QMessageBox>
#include <QFileDialog>

QCodeEditor::QCodeEditor( QWidget* parent ) :
QWidget( parent ),
textChangedFlag( false )
{
	QVBoxLayout* verticalLayout = new QVBoxLayout( this );
	verticalLayout->setContentsMargins( 0, 0, 0, 0 );
	setLayout( verticalLayout );
	textEdit = new QCodeTextEdit( this );

	QFont font;
	font.setFamily( QStringLiteral( "Consolas" ) );
	font.setPointSize( 9 );
	textEdit->setFont( font );
	textEdit->setLineWrapMode( QPlainTextEdit::NoWrap );
	textEdit->setTabStopWidth( 16 );
	textEdit->setWordWrapMode( QTextOption::NoWrap );
	verticalLayout->addWidget( textEdit );

	connect( textEdit, SIGNAL( textChanged() ), this, SLOT( textEditChanged() ) );
}

QCodeEditor::~QCodeEditor()
{}

QString QCodeEditor::getPlainText() const
{
	return textEdit->toPlainText();
}

void QCodeEditor::open( const QString& filename )
{
	QCodeSyntaxHighlighter* xmlSyntaxHighlighter = new QCodeSyntaxHighlighter( textEdit->document(), QCodeSyntaxHighlighter::detectLanguage( filename ) );

	QFile f( filename );
	if ( f.open( QFile::ReadOnly | QFile::Text ) )
	{
		QTextStream str( &f );
		QString data = str.readAll();
		textEdit->setPlainText( data );
		fileName = filename;
		textChangedFlag = false;
	}
	else xo_error( "Could not open file: " + filename.toStdString() );
}

void QCodeEditor::openDialog( const QString& folder, const QString& fileTypes )
{
	auto fn = QFileDialog::getOpenFileName( this, "Open File", folder, fileTypes );
	if ( !fn.isEmpty() )
		open( fn );
}

void QCodeEditor::save()
{
	QFile file( fileName );
	if ( !file.open( QIODevice::WriteOnly ) )
	{
		QMessageBox::critical( this, "Error writing file", "Could not open file " + fileName );
		return;
	}
	else
	{
		QTextStream stream( &file );
		stream << textEdit->toPlainText();
		stream.flush();
		file.close();
		textChangedFlag = false;
	}
}

void QCodeEditor::saveAs( const QString& fn )
{
	if ( getFileFormat( fn ) != getFileFormat( fileName ) )
	{
		std::stringstream stri( textEdit->toPlainText().toStdString() );
		xo::prop_node pn;
		stri >> xo::prop_node_deserializer( getFileFormat( fileName ), pn );
		std::stringstream stro;
		stro << xo::prop_node_serializer( getFileFormat( fn ), pn );

		QCodeSyntaxHighlighter* xmlSyntaxHighlighter = new QCodeSyntaxHighlighter( textEdit->document(), QCodeSyntaxHighlighter::detectLanguage( fn ) );
		textEdit->setPlainText( QString( stro.str().c_str() ) );
	}

	fileName = fn;
	save();
}

QString QCodeEditor::getTitle()
{
	return QFileInfo( fileName ).fileName() + ( hasTextChanged() ? "*" : "" );
}

void QCodeEditor::textEditChanged()
{
	if ( !textChangedFlag )
	{
		textChangedFlag = true;
		emit textChanged();
	}
}

xo::file_format QCodeEditor::getFileFormat( const QString& filename ) const
{
	auto ext = xo::path( filename.toStdString() ).extension();
	if ( ext == "xml" )
		return xo::file_format::xml;
	else if ( ext == "zml" )
		return xo::file_format::zml;
	else return xo::file_format::unknown;
}

//
// BasicXMLSyntaxHighlighter
//

QCodeSyntaxHighlighter::QCodeSyntaxHighlighter( QObject* parent, Language l ) : QSyntaxHighlighter( parent )
{
	setLanguage( l );
}

QCodeSyntaxHighlighter::QCodeSyntaxHighlighter( QTextDocument* parent, Language l ) : QSyntaxHighlighter( parent )
{
	setLanguage( l );
}

void QCodeSyntaxHighlighter::highlightBlock( const QString &text )
{
	if ( language == XML )
	{
		// Special treatment for xml element regex as we use captured text to emulate lookbehind
		int xmlElementIndex = m_xmlElementRegex.indexIn( text );
		while ( xmlElementIndex >= 0 )
		{
			int matchedPos = m_xmlElementRegex.pos( 1 );
			int matchedLength = m_xmlElementRegex.cap( 1 ).length();
			setFormat( matchedPos, matchedLength, m_ElementFormat );
			xmlElementIndex = m_xmlElementRegex.indexIn( text, matchedPos + matchedLength );
		}
		highlightByRegex( m_NumberFormat, m_NumberRegex, text );
		highlightByRegex( m_AttributeFormat, m_xmlAttributeRegex, text );
	}
	else
	{
		highlightByRegex( m_NumberFormat, m_NumberRegex, text );
		highlightByRegex( m_AttributeFormat, m_xmlAttributeRegex, text );
		highlightByRegex( m_ElementFormat, m_xmlElementRegex, text );
	}

	// Highlight xml keywords *after* xml elements to fix any occasional / captured into the enclosing element
	for ( auto& regex : m_xmlKeywordRegexes )
		highlightByRegex( m_KeywordFormat, regex, text );

	highlightByRegex( m_ValueFormat, m_xmlValueRegex, text );
	highlightByRegex( m_SpecialFormat, m_SpecialRegex, text );

	if ( language == ZML )
	{
		int i = m_xmlCommentRegex.indexIn( text );
		while  ( i >= 0 )
		{
			int quotes_before = 0;
			for ( int x = 0; x <= i; ++x )
				quotes_before += int( text[ x ] == '\"' );
			if ( quotes_before % 2 == 0 )
			{
				setFormat( i, m_xmlCommentRegex.matchedLength(), m_CommentFormat );
				break;
			}
			else i = m_xmlCommentRegex.indexIn( text, i + 1 );
		}
	}
	else
	{
		highlightByRegex( m_CommentFormat, m_xmlCommentRegex, text );
	}
}

void QCodeSyntaxHighlighter::highlightByRegex( const QTextCharFormat & format, const QRegExp & regex, const QString & text )
{
	int index = regex.indexIn( text );
	while ( index >= 0 )
	{
		int matchedLength = regex.matchedLength();
		setFormat( index, matchedLength, format );
		index = regex.indexIn( text, index + matchedLength );
	}
}

void QCodeSyntaxHighlighter::setRegexes()
{
	switch ( language )
	{
	case XML:
		m_xmlElementRegex.setPattern( "<[\\s]*[/]?[\\s]*([^\\n]\\w*)(?=[\\s/>])" );
		m_xmlAttributeRegex.setPattern( "\\w+(?=\\=)" );
		m_xmlValueRegex.setPattern( "\"[^\\n\"]+\"(?=[\\s/>])" );
		m_xmlCommentRegex.setPattern( "<!--[^\\n]*-->" );
		m_SpecialRegex.setPattern( "/.^/" );
		m_xmlKeywordRegexes = QList<QRegExp>() << QRegExp( "<\\?" ) << QRegExp( "/>" ) << QRegExp( ">" ) << QRegExp( "<" ) << QRegExp( "</" ) << QRegExp( "\\?>" );
		break;
	case ZML:
		m_xmlElementRegex.setPattern( "\\w+\\s*\\=\\s*\\{" );
		m_xmlAttributeRegex.setPattern( "\\w+\\s*(\\=)" );
		m_xmlValueRegex.setPattern( "\"[^\\n\"]+\"" );
		m_xmlCommentRegex.setPattern( ";[^\\n]*" );
		m_SpecialRegex.setPattern( "#\\w+" );
		m_xmlKeywordRegexes = QList<QRegExp>() << QRegExp( "\\{" ) << QRegExp( "\\}" ) << QRegExp( "\\[" ) << QRegExp( "\\]" ) << QRegExp( "\\=" );
		break;
	default:
		xo_error( "Unsupported language" );
	}

	m_NumberRegex.setPattern( "\\b([-+]?[\\.\\d]+)" );
}

void QCodeSyntaxHighlighter::setFormats()
{
	m_KeywordFormat.setForeground( Qt::darkGray );
	m_ElementFormat.setForeground( Qt::darkBlue );
	m_ElementFormat.setFontWeight( QFont::Bold );
	m_AttributeFormat.setForeground( Qt::darkBlue );
	//m_AttributeFormat.setFontWeight( QFont::Bold );
	m_ValueFormat.setForeground( Qt::darkRed );
	m_CommentFormat.setForeground( Qt::darkGreen );
	m_CommentFormat.setFontItalic( true );
	m_NumberFormat.setForeground( Qt::darkMagenta );
	m_SpecialFormat.setForeground( Qt::blue );
	m_SpecialFormat.setFontItalic( true );
	m_SpecialFormat.setFontWeight( QFont::Bold );
}

void QCodeSyntaxHighlighter::setLanguage( Language l )
{
	language = l;
	setRegexes();
	setFormats();
}

QCodeSyntaxHighlighter::Language QCodeSyntaxHighlighter::detectLanguage( const QString& filename )
{
	auto ext = xo::path( filename.toStdString() ).extension();
	if ( ext == "xml" )
		return XML;
	else if ( ext == "zml" )
		return ZML;
	else return ZML;
}

//
// QCodeTextEdit
//

QCodeTextEdit::QCodeTextEdit( QWidget* parent ) : QPlainTextEdit( parent )
{
	lineNumberArea = new LineNumberArea( this );

	connect( this, SIGNAL( blockCountChanged( int ) ), this, SLOT( updateLineNumberAreaWidth( int ) ) );
	connect( this, SIGNAL( updateRequest( QRect, int ) ), this, SLOT( updateLineNumberArea( QRect, int ) ) );

	updateLineNumberAreaWidth( 0 );
}

void QCodeTextEdit::lineNumberAreaPaintEvent( QPaintEvent *event )
{
	QPainter painter( lineNumberArea );
	painter.fillRect( event->rect(), Qt::lightGray );

	QTextBlock block = firstVisibleBlock();
	int blockNumber = block.blockNumber();
	int top = (int)blockBoundingGeometry( block ).translated( contentOffset() ).top();
	int bottom = top + (int)blockBoundingRect( block ).height();

	while ( block.isValid() && top <= event->rect().bottom() ) {
		if ( block.isVisible() && bottom >= event->rect().top() ) {
			QString number = QString::number( blockNumber + 1 );
			painter.setPen( Qt::black );
			painter.drawText( 0, top, lineNumberArea->width() - 2, fontMetrics().height(),
				Qt::AlignRight, number );
		}

		block = block.next();
		top = bottom;
		bottom = top + (int)blockBoundingRect( block ).height();
		++blockNumber;
	}
}

int QCodeTextEdit::lineNumberAreaWidth()
{
	int digits = 1;
	int max = qMax( 1, blockCount() );
	while ( max >= 10 ) {
		max /= 10;
		++digits;
	}

	int space = 4 + fontMetrics().width( QLatin1Char( '9' ) ) * digits;

	return space;
}

void QCodeTextEdit::updateLineNumberAreaWidth( int newBlockCount )
{
	setViewportMargins( lineNumberAreaWidth(), 0, 0, 0 );
}

void QCodeTextEdit::updateLineNumberArea( const QRect& rect, int dy )
{
	if ( dy )
		lineNumberArea->scroll( 0, dy );
	else
		lineNumberArea->update( 0, rect.y(), lineNumberArea->width(), rect.height() );

	if ( rect.contains( viewport()->rect() ) )
		updateLineNumberAreaWidth( 0 );
}

void QCodeTextEdit::resizeEvent( QResizeEvent *event )
{
	QPlainTextEdit::resizeEvent( event );
	QRect cr = contentsRect();
	lineNumberArea->setGeometry( QRect( cr.left(), cr.top(), lineNumberAreaWidth(), cr.height() ) );
}

void QCodeTextEdit::keyPressEvent( QKeyEvent *e )
{
	if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
	{
		auto line = textCursor().block().text().toStdString();
		int tabs = 0;
		while ( tabs < line.size() && line[ tabs ] == '\t' )
			++tabs;
		QPlainTextEdit::keyPressEvent( e );
		QPlainTextEdit::insertPlainText( QString( tabs, '\t' ) );
	}
	else QPlainTextEdit::keyPressEvent( e );
}
<|endoftext|>


<commit_before>#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <xorg/gtest/xorg-gtest.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XTest.h>

#include "xorg-conf.h"
#include "xit-server.h"
#include "helpers.h"

using namespace xorg::testing;

static void disable_device_property(Display *dpy, std::string name)
{
    Atom prop = XInternAtom(dpy, "Device Enabled", True);
    ASSERT_NE(prop, (Atom)None);

    int deviceid;
    ASSERT_EQ(FindInputDeviceByName(dpy, name, &deviceid), 1);
    unsigned char data = 0;
    XIChangeProperty(dpy, deviceid, prop, XA_INTEGER, 8,
                     PropModeReplace, &data, 1);
    XSync(dpy, False);
}

TEST(XTest, DisabledDevicesProperty)
{
    XORG_TESTCASE("Disabling an XTest device through properties "
                 "should not be possible\n"
                 "https://bugs.freedesktop.org/show_bug.cgi?id=56380");

    XITServer server;
    XOrgConfig config;
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    ASSERT_TRUE(dpy);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core pointer");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core keyboard");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core XTEST pointer");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core XTEST keyboard");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    /* if disabled, this will crash the server */
    XTestFakeButtonEvent(dpy, 1, 1, 0);
    XTestFakeButtonEvent(dpy, 1, 2, 0);
    XTestFakeKeyEvent(dpy, 64, 1, 0);
    XTestFakeKeyEvent(dpy, 64, 2, 0);
    XSync(dpy, False);

    config.RemoveConfig();
}

static void disable_device_devctl(Display *dpy, std::string name)
{
    XDeviceEnableControl ctl;
    ctl.control = DEVICE_ENABLE;
    ctl.length = sizeof(XDeviceEnableControl);
    ctl.enable = 0;

    int deviceid;
    ASSERT_EQ(FindInputDeviceByName(dpy, name, &deviceid), 1);
    XDevice *dev = XOpenDevice(dpy, deviceid);
    ASSERT_TRUE(dev);
    XChangeDeviceControl(dpy, dev, DEVICE_ENABLE,
                         reinterpret_cast<XDeviceControl*>(&ctl));
    XSync(dpy, False);
}

TEST(XTest, DisabledDevicesCtl)
{
    XORG_TESTCASE("Disabling an XTest device through device "
                  "controls should not possible\n"
                  "https://bugs.freedesktop.org/show_bug.cgi?id=56380");

    XITServer server;
    XOrgConfig config;
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    ASSERT_TRUE(dpy);

    XSync(dpy, False);

    SetErrorTrap(dpy);
    disable_device_devctl(dpy, "Virtual core XTEST pointer");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadMatch);

    SetErrorTrap(dpy);
    disable_device_devctl(dpy, "Virtual core XTEST keyboard");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadMatch);

    /* if disabled, this will crash the server */
    XTestFakeButtonEvent(dpy, 1, 1, 0);
    XTestFakeButtonEvent(dpy, 1, 2, 0);
    XTestFakeKeyEvent(dpy, 64, 1, 0);
    XTestFakeKeyEvent(dpy, 64, 2, 0);
    XSync(dpy, False);

    config.RemoveConfig();
}
<commit_msg>server/xtest: add test for xtest release on #28808<commit_after>#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <xorg/gtest/xorg-gtest.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XTest.h>

#include "xorg-conf.h"
#include "xit-server-input-test.h"
#include "device-interface.h"
#include "xit-server.h"
#include "xit-event.h"
#include "helpers.h"

using namespace xorg::testing;

static void disable_device_property(Display *dpy, std::string name)
{
    Atom prop = XInternAtom(dpy, "Device Enabled", True);
    ASSERT_NE(prop, (Atom)None);

    int deviceid;
    ASSERT_EQ(FindInputDeviceByName(dpy, name, &deviceid), 1);
    unsigned char data = 0;
    XIChangeProperty(dpy, deviceid, prop, XA_INTEGER, 8,
                     PropModeReplace, &data, 1);
    XSync(dpy, False);
}

TEST(XTest, DisabledDevicesProperty)
{
    XORG_TESTCASE("Disabling an XTest device through properties "
                 "should not be possible\n"
                 "https://bugs.freedesktop.org/show_bug.cgi?id=56380");

    XITServer server;
    XOrgConfig config;
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    ASSERT_TRUE(dpy);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core pointer");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core keyboard");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core XTEST pointer");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    SetErrorTrap(dpy);
    disable_device_property(dpy, "Virtual core XTEST keyboard");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadAccess);

    /* if disabled, this will crash the server */
    XTestFakeButtonEvent(dpy, 1, 1, 0);
    XTestFakeButtonEvent(dpy, 1, 2, 0);
    XTestFakeKeyEvent(dpy, 64, 1, 0);
    XTestFakeKeyEvent(dpy, 64, 2, 0);
    XSync(dpy, False);

    config.RemoveConfig();
}

static void disable_device_devctl(Display *dpy, std::string name)
{
    XDeviceEnableControl ctl;
    ctl.control = DEVICE_ENABLE;
    ctl.length = sizeof(XDeviceEnableControl);
    ctl.enable = 0;

    int deviceid;
    ASSERT_EQ(FindInputDeviceByName(dpy, name, &deviceid), 1);
    XDevice *dev = XOpenDevice(dpy, deviceid);
    ASSERT_TRUE(dev);
    XChangeDeviceControl(dpy, dev, DEVICE_ENABLE,
                         reinterpret_cast<XDeviceControl*>(&ctl));
    XSync(dpy, False);
}

TEST(XTest, DisabledDevicesCtl)
{
    XORG_TESTCASE("Disabling an XTest device through device "
                  "controls should not possible\n"
                  "https://bugs.freedesktop.org/show_bug.cgi?id=56380");

    XITServer server;
    XOrgConfig config;
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    ASSERT_TRUE(dpy);

    XSync(dpy, False);

    SetErrorTrap(dpy);
    disable_device_devctl(dpy, "Virtual core XTEST pointer");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadMatch);

    SetErrorTrap(dpy);
    disable_device_devctl(dpy, "Virtual core XTEST keyboard");
    ASSERT_ERROR(ReleaseErrorTrap(dpy), BadMatch);

    /* if disabled, this will crash the server */
    XTestFakeButtonEvent(dpy, 1, 1, 0);
    XTestFakeButtonEvent(dpy, 1, 2, 0);
    XTestFakeKeyEvent(dpy, 64, 1, 0);
    XTestFakeKeyEvent(dpy, 64, 2, 0);
    XSync(dpy, False);

    config.RemoveConfig();
}

class XTestPhysicalDeviceTest : public XITServerInputTest,
                                public DeviceInterface {
public:
    /**
     * Initializes a standard mouse device.
     */
    virtual void SetUp() {
        SetDevice("mice/PIXART-USB-OPTICAL-MOUSE-HWHEEL.desc");
        XITServerInputTest::SetUp();
    }

    virtual void SetUpConfigAndLog() {

        config.AddDefaultScreenWithDriver();
        config.AddInputSection("evdev", "--device--",
                               "Option \"CorePointer\" \"on\"\n"
                               "Option \"Device\" \"" + dev->GetDeviceNode() + "\"");
        /* add default keyboard device to avoid server adding our device again */
        config.AddInputSection("kbd", "kbd-device",
                               "Option \"CoreKeyboard\" \"on\"\n");
        config.WriteConfig();
    }
};

TEST_F(XTestPhysicalDeviceTest, ReleaseXTestOnPhysicalRelease)
{
    XORG_TESTCASE("Create a pointer device.\n"
                  "Post button press through pointer device.\n"
                  "Post button press through XTest device.\n"
                  "Post button release through pointer device.\n"
                  "Expect ButtonRelease, expect button state up\n");

    ::Display *dpy = Display();
    XSynchronize(dpy, True);

    XSelectInput(dpy, DefaultRootWindow(dpy), ButtonPressMask|ButtonReleaseMask);

    dev->PlayOne(EV_KEY, BTN_LEFT, 1, True);

    XITEvent<XButtonEvent> press(dpy, ButtonPress);
    ASSERT_TRUE(press.ev);
    ASSERT_EQ(press.ev->button, 1U);

    XTestFakeButtonEvent(dpy, 1, 1, 0);

    dev->PlayOne(EV_KEY, BTN_LEFT, 0, True);

    XITEvent<XButtonEvent> release(dpy, ButtonRelease);
    ASSERT_TRUE(release.ev);
    ASSERT_EQ(release.ev->button, 1U);

    Window root, child;
    int root_x, root_y, win_x, win_y;
    unsigned int mask;
    XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
                  &root_x, &root_y, &win_x, &win_y, &mask);
    ASSERT_EQ(mask & Button1Mask, 0U);
}
<|endoftext|>


<commit_before>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Main
#include <boost/test/unit_test.hpp>

#include <meas.h>
#include <memstorage.h>
#include <string>
#include <thread>
#include <iostream>

const size_t copies_count = 100;

void checkAll(memseries::Meas::MeasList res, std::string msg, memseries::Time from, memseries::Time to, memseries::Time  step) {
	for (auto i = from; i < to; i += step) {
		size_t count = 0;
		for (auto &m : res) {
			if ((m.id == i) && (m.flag == i) && (m.time == i)) {
				count++;
			}

		}
		if (count < copies_count) {
			BOOST_CHECK_EQUAL(copies_count, count);
		}
	}
}

void storage_test_check(memseries::storage::AbstractStorage *as, memseries::Time from, memseries::Time to, memseries::Time  step) {
	auto m = memseries::Meas::empty();
	size_t total_count = 0;

	for (auto i = from; i < to; i += step) {
		m.id = i;
		m.flag = memseries::Flag(i);
		m.time = i;
		m.value = 0;
		for (size_t j = 1; j < copies_count+1; j++) {
			BOOST_CHECK(as->append(m).writed == 1);
			total_count++;
			m.value = j;
		}
	}

	memseries::Meas::MeasList all{};
	as->readInterval(from, to)->readAll(&all);
	BOOST_CHECK_EQUAL(all.size(), total_count+1); // [_from,to, by step] + 1 from timePoint=0 (meas with time=0.).

	checkAll(all, "readAll error: ", from, to, step);

	memseries::IdArray ids{};
	all.clear();
	as->readInterval(ids, 0, from, to)->readAll(&all);
	BOOST_CHECK_EQUAL(all.size(), total_count+1);

	checkAll(all, "read error: ", from, to, step);

	ids.push_back(from + step);
	memseries::Meas::MeasList fltr_res{};
	as->readInterval(ids, 0, from, to)->readAll(&fltr_res);

	BOOST_CHECK_EQUAL(fltr_res.size(), copies_count);

	BOOST_CHECK_EQUAL(fltr_res.front().id, ids[0]);

	fltr_res.clear();
	as->readInterval(ids, memseries::Flag(to + 1), from, to)->readAll(&fltr_res);
	BOOST_CHECK_EQUAL(fltr_res.size(), size_t(0));

	all.clear();
	as->readInTimePoint(to)->readAll(&all);
	size_t ids_count = (size_t)((to - from) / step);
	//TODO ==(to-from)/step
	BOOST_CHECK_EQUAL(all.size(), ids_count);

	memseries::IdArray emptyIDs{};
	fltr_res.clear();
	as->readInTimePoint(to)->readAll(&fltr_res);
	BOOST_CHECK_EQUAL(fltr_res.size(), ids_count);
}

BOOST_AUTO_TEST_CASE(inFilter) {
	{
		BOOST_CHECK(memseries::in_filter(0, 100));
		BOOST_CHECK(!memseries::in_filter(1, 100));
	}
}

BOOST_AUTO_TEST_CASE(MemoryStorage) {
	{
		auto ms = new memseries::storage::MemoryStorage{ 500 };
		const memseries::Time from = 0;
		const memseries::Time to = 100;
		const memseries::Time step = 2;
		storage_test_check(ms, from, to, step);
		BOOST_CHECK_EQUAL(ms->chunks_size(), (to - from) / step); // id per chunk.
		delete ms;
	}
}

void thread_writer(memseries::Id id, memseries::Time from, memseries::Time to, memseries::Time step, memseries::storage::MemoryStorage *ms)
{
	auto m = memseries::Meas::empty();
	for (auto i = from; i < to; i += step) {
		m.id = id;
		m.flag = memseries::Flag(i);
		m.time = i;
		m.value = 0;
		for (size_t j = 0; j < copies_count; j++) {
			ms->append(m);
			m.value = j;
		}
	}
}

//BOOST_AUTO_TEST_CASE(MultiThread)
//{
//	auto ms = new memseries::storage::MemoryStorage{ 500 };
//	std::thread t1(thread_writer, 0, 0, 100, 2, ms);
//	std::thread t2(thread_writer, 1, 0, 100, 2, ms);
//	std::thread t3(thread_writer, 2, 0, 100, 2, ms);
//	std::thread t4(thread_writer, 3, 0, 100, 2, ms);

//	t1.join();
//	t2.join();
//	t3.join();
//	t4.join();
//	delete ms;
//}

BOOST_AUTO_TEST_CASE(ReadInterval)
{
	auto ds = new memseries::storage::MemoryStorage{ 500 };
	memseries::Meas m;
	{
		m.id = 1; m.time = 1;
		ds->append(m);
		m.id = 2; m.time = 2;
		ds->append(m);

		m.id = 4; m.time = 4;
		ds->append(m);
		m.id = 5; m.time = 5;
		ds->append(m);
		m.id = 55; m.time = 5;
		ds->append(m);

		{
			auto tp_reader = ds->readInTimePoint(6);
			memseries::Meas::MeasList output_in_point{};
			tp_reader->readAll(&output_in_point);

			BOOST_CHECK_EQUAL(output_in_point.size(), size_t(5));
		}
		{

			auto tp_reader = ds->readInTimePoint(3);
			memseries::Meas::MeasList output_in_point{};
			tp_reader->readAll(&output_in_point);

			BOOST_CHECK_EQUAL(output_in_point.size(), size_t(2));
			for (auto v : output_in_point) {
				BOOST_CHECK(v.time <= 3);
			}
		}
		auto reader = ds->readInterval(3, 5);
		memseries::Meas::MeasList output{};
		reader->readAll(&output);
		BOOST_CHECK_EQUAL(output.size(), size_t(5));
	}
	{
		m.id = 1; m.time = 6;
		ds->append(m);
		m.id = 2; m.time = 7;
		ds->append(m);

		m.id = 4; m.time = 9;
		ds->append(m);
		m.id = 5; m.time = 10;
		ds->append(m);
        m.id = 6; m.time = 10;
		ds->append(m);
		{

			auto tp_reader = ds->readInTimePoint(8);
			memseries::Meas::MeasList output_in_point{};
			tp_reader->readAll(&output_in_point);

			BOOST_CHECK_EQUAL(output_in_point.size(), size_t(5));
			for (auto v : output_in_point) {
				BOOST_CHECK(v.time <= 8);
			}
		}

        auto reader = ds->readInterval(memseries::IdArray{ 1,2,4,5,55 }, 0, 8, 10);
        memseries::Meas::MeasList output{};
        reader->readAll(&output);
        BOOST_CHECK_EQUAL(output.size(), size_t(7));
        if(output.size()!=size_t(7)){
            std::cout<<" ERROR!!!!"<<std::endl;
            reader->readNext(nullptr);
        }
        for(memseries::Meas v:output){
            std::cout<<" id:"<<v.id
                    <<" flg:"<<v.flag
                   <<" v:"<<v.value
                  <<" t:"<<v.time<<std::endl;
        }

	}
	delete ds;
}
<commit_msg>tests<commit_after>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Main
#include <boost/test/unit_test.hpp>

#include <meas.h>
#include <memstorage.h>
#include <string>
#include <thread>
#include <iostream>

const size_t copies_count = 100;

void checkAll(memseries::Meas::MeasList res, std::string msg, memseries::Time from, memseries::Time to, memseries::Time  step) {
	for (auto i = from; i < to; i += step) {
		size_t count = 0;
		for (auto &m : res) {
			if ((m.id == i) && (m.flag == i) && (m.time == i)) {
				count++;
			}

		}
		if (count < copies_count) {
			BOOST_CHECK_EQUAL(copies_count, count);
		}
	}
}

void storage_test_check(memseries::storage::AbstractStorage *as, memseries::Time from, memseries::Time to, memseries::Time  step) {
	auto m = memseries::Meas::empty();
	size_t total_count = 0;

	for (auto i = from; i < to; i += step) {
		m.id = i;
		m.flag = memseries::Flag(i);
		m.time = i;
		m.value = 0;
		for (size_t j = 1; j < copies_count+1; j++) {
			BOOST_CHECK(as->append(m).writed == 1);
			total_count++;
			m.value = j;
		}
	}

	memseries::Meas::MeasList all{};
	as->readInterval(from, to)->readAll(&all);
    BOOST_CHECK_EQUAL(all.size(), total_count);

	checkAll(all, "readAll error: ", from, to, step);

	memseries::IdArray ids{};
	all.clear();
	as->readInterval(ids, 0, from, to)->readAll(&all);
    BOOST_CHECK_EQUAL(all.size(), total_count);

	checkAll(all, "read error: ", from, to, step);

	ids.push_back(from + step);
	memseries::Meas::MeasList fltr_res{};
	as->readInterval(ids, 0, from, to)->readAll(&fltr_res);

	BOOST_CHECK_EQUAL(fltr_res.size(), copies_count);

	BOOST_CHECK_EQUAL(fltr_res.front().id, ids[0]);

	fltr_res.clear();
	as->readInterval(ids, memseries::Flag(to + 1), from, to)->readAll(&fltr_res);
	BOOST_CHECK_EQUAL(fltr_res.size(), size_t(0));

	all.clear();
	as->readInTimePoint(to)->readAll(&all);
	size_t ids_count = (size_t)((to - from) / step);
	//TODO ==(to-from)/step
	BOOST_CHECK_EQUAL(all.size(), ids_count);

	memseries::IdArray emptyIDs{};
	fltr_res.clear();
	as->readInTimePoint(to)->readAll(&fltr_res);
	BOOST_CHECK_EQUAL(fltr_res.size(), ids_count);
}

BOOST_AUTO_TEST_CASE(inFilter) {
	{
		BOOST_CHECK(memseries::in_filter(0, 100));
		BOOST_CHECK(!memseries::in_filter(1, 100));
	}
}

BOOST_AUTO_TEST_CASE(MemoryStorage) {
	{
		auto ms = new memseries::storage::MemoryStorage{ 500 };
		const memseries::Time from = 0;
		const memseries::Time to = 100;
		const memseries::Time step = 2;
		storage_test_check(ms, from, to, step);
		BOOST_CHECK_EQUAL(ms->chunks_size(), (to - from) / step); // id per chunk.
		delete ms;
	}
}

void thread_writer(memseries::Id id, memseries::Time from, memseries::Time to, memseries::Time step, memseries::storage::MemoryStorage *ms)
{
	auto m = memseries::Meas::empty();
	for (auto i = from; i < to; i += step) {
		m.id = id;
		m.flag = memseries::Flag(i);
		m.time = i;
		m.value = 0;
		for (size_t j = 0; j < copies_count; j++) {
			ms->append(m);
			m.value = j;
		}
	}
}

//BOOST_AUTO_TEST_CASE(MultiThread)
//{
//	auto ms = new memseries::storage::MemoryStorage{ 500 };
//	std::thread t1(thread_writer, 0, 0, 100, 2, ms);
//	std::thread t2(thread_writer, 1, 0, 100, 2, ms);
//	std::thread t3(thread_writer, 2, 0, 100, 2, ms);
//	std::thread t4(thread_writer, 3, 0, 100, 2, ms);

//	t1.join();
//	t2.join();
//	t3.join();
//	t4.join();
//	delete ms;
//}

BOOST_AUTO_TEST_CASE(ReadInterval)
{
	auto ds = new memseries::storage::MemoryStorage{ 500 };
	memseries::Meas m;
	{
		m.id = 1; m.time = 1;
		ds->append(m);
		m.id = 2; m.time = 2;
		ds->append(m);

		m.id = 4; m.time = 4;
		ds->append(m);
		m.id = 5; m.time = 5;
		ds->append(m);
		m.id = 55; m.time = 5;
		ds->append(m);

		{
			auto tp_reader = ds->readInTimePoint(6);
			memseries::Meas::MeasList output_in_point{};
			tp_reader->readAll(&output_in_point);

			BOOST_CHECK_EQUAL(output_in_point.size(), size_t(5));
		}
		{

			auto tp_reader = ds->readInTimePoint(3);
			memseries::Meas::MeasList output_in_point{};
			tp_reader->readAll(&output_in_point);

			BOOST_CHECK_EQUAL(output_in_point.size(), size_t(2));
			for (auto v : output_in_point) {
				BOOST_CHECK(v.time <= 3);
			}
		}
		auto reader = ds->readInterval(3, 5);
		memseries::Meas::MeasList output{};
		reader->readAll(&output);
		BOOST_CHECK_EQUAL(output.size(), size_t(5));
	}
	{
		m.id = 1; m.time = 6;
		ds->append(m);
		m.id = 2; m.time = 7;
		ds->append(m);

		m.id = 4; m.time = 9;
		ds->append(m);
		m.id = 5; m.time = 10;
		ds->append(m);
        m.id = 6; m.time = 10;
		ds->append(m);
		{

			auto tp_reader = ds->readInTimePoint(8);
			memseries::Meas::MeasList output_in_point{};
			tp_reader->readAll(&output_in_point);

			BOOST_CHECK_EQUAL(output_in_point.size(), size_t(5));
			for (auto v : output_in_point) {
				BOOST_CHECK(v.time <= 8);
			}
		}

        auto reader = ds->readInterval(memseries::IdArray{ 1,2,4,5,55 }, 0, 8, 10);
        memseries::Meas::MeasList output{};
        reader->readAll(&output);
        BOOST_CHECK_EQUAL(output.size(), size_t(7));
        if(output.size()!=size_t(7)){
            std::cout<<" ERROR!!!!"<<std::endl;
            //reader->readNext(nullptr);
            for(memseries::Meas v:output){
                std::cout<<" id:"<<v.id
                        <<" flg:"<<v.flag
                       <<" v:"<<v.value
                      <<" t:"<<v.time<<std::endl;
            }
        }

	}
	delete ds;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <gtest/gtest.h>
#include "../pipeline.h"
#include "../channel.h"
#include "../scheduler.h"
#include "../shell.h"
#include <thread>
#include <asyncply/run.h>

class ChannelTest : testing::Test { };

using cmd = cu::pipeline<int>;

cmd::link generator()
{
	return [](cmd::in&, cmd::out& yield)
	{
		for (auto& s : {100, 200, 300})
		{
			std::cout << "I am generator and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link1()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link1 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link2()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link2 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link3()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link3 and push " << s << std::endl;
			yield(s);
		}
	};
}

TEST(ChannelTest, pipeline)
{
	cmd(generator(), link1(), link2(), link3());
}

TEST(ChannelTest, goroutines_consumer)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 8);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));
	sch.spawn([&](auto& yield) {
		// send
		for(int i=1; i<=50; ++i)
		{
			std::cout << "sending: " << i << std::endl;
			go(std::to_string(i));
			if(go.full())
				yield();
		}
		go.close();
	});
	sch.spawn([&](auto& yield) {
		for(;;)
		{
-			auto& data = go.get();
 -			if(!data)
 -			{
 -				std::cout << "channel closed" << std::endl;
 -				break;
 -			}
 -			else
 -			{
 -				std::cout << "recving: " << *data << std::endl;
				if(go.empty())
					yield();
 -			}
		}
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler)
{
	cu::scheduler sch;
	cu::semaphore person1(sch);
	cu::semaphore person2(sch);
	cu::semaphore other(sch);
	// person2
	sch.spawn([&](auto& yield) {
		std::cout << "Hola person1" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "que tal ?" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "me alegro" << std::endl;
		person2.notify(yield);
		//
		other.notify(yield);
	});
	// person1
	sch.spawn([&](auto& yield) {
		//
		person2.wait(yield);
		std::cout << "Hola person2" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "bien!" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "y yo ^^" << std::endl;
		//
		other.notify(yield);
	});
	// other
	sch.spawn([&](auto& yield) {
		//
		other.wait(yield);
		other.wait(yield);
		std::cout << "parar!!! tengo algo importante" << std::endl;
	});
	sch.run_until_complete();
}
<commit_msg>Update test_channel.cpp<commit_after>#include <iostream>
#include <gtest/gtest.h>
#include "../pipeline.h"
#include "../channel.h"
#include "../scheduler.h"
#include "../shell.h"
#include <thread>
#include <asyncply/run.h>

class ChannelTest : testing::Test { };

using cmd = cu::pipeline<int>;

cmd::link generator()
{
	return [](cmd::in&, cmd::out& yield)
	{
		for (auto& s : {100, 200, 300})
		{
			std::cout << "I am generator and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link1()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link1 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link2()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link2 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link3()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link3 and push " << s << std::endl;
			yield(s);
		}
	};
}

TEST(ChannelTest, pipeline)
{
	cmd(generator(), link1(), link2(), link3());
}

TEST(ChannelTest, goroutines_consumer)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 8);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));
	sch.spawn([&](auto& yield) {
		// send
		for(int i=1; i<=50; ++i)
		{
			std::cout << "sending: " << i << std::endl;
			go(std::to_string(i));
			if(go.full())
				yield();
		}
		go.close();
	});
	sch.spawn([&](auto& yield) {
		for(;;)
		{
-			auto data = go.get();
 -			if(data)
 -			{
 -				std::cout << "recving: " << *data << std::endl;
				if(go.empty())
					yield();
 -			}
 -			else
 -			{
 -				std::cout << "channel closed" << std::endl;
 -				break;
 -			}
		}
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler)
{
	cu::scheduler sch;
	cu::semaphore person1(sch);
	cu::semaphore person2(sch);
	cu::semaphore other(sch);
	// person2
	sch.spawn([&](auto& yield) {
		std::cout << "Hola person1" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "que tal ?" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "me alegro" << std::endl;
		person2.notify(yield);
		//
		other.notify(yield);
	});
	// person1
	sch.spawn([&](auto& yield) {
		//
		person2.wait(yield);
		std::cout << "Hola person2" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "bien!" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "y yo ^^" << std::endl;
		//
		other.notify(yield);
	});
	// other
	sch.spawn([&](auto& yield) {
		//
		other.wait(yield);
		other.wait(yield);
		std::cout << "parar!!! tengo algo importante" << std::endl;
	});
	sch.run_until_complete();
}
<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip>
#include <algorithm>
#include <toml/region.hpp>
#include <toml/result.hpp>

#define TOML11_TEST_LEX_ACCEPT(lxr, tkn, expct)                                \
do {                                                                           \
    const std::string token   (tkn);                                           \
    const std::string expected(expct);                                         \
    toml::detail::location<std::string> loc(token);                            \
    const auto result = lxr::invoke(loc);                                      \
    BOOST_CHECK(result.is_ok());                                               \
    if(result.is_ok()){                                                        \
    const auto region = result.unwrap();                                       \
    BOOST_CHECK_EQUAL(region.str(), expected);                                 \
    BOOST_CHECK_EQUAL(region.str().size(), expected.size());                   \
    BOOST_CHECK_EQUAL(std::distance(loc.begin(), loc.iter()), region.size());  \
    } else {                                                                   \
    std::cerr << "lexer " << lxr::pattern() << " failed with input `";         \
    std::cerr << token << "`. expected `" << expected << "`\n";                \
    std::cerr << "reason: " << result.unwrap_err() << '\n';                    \
    }                                                                          \
} while(false);                                                                \
/**/

#define TOML11_TEST_LEX_REJECT(lxr, tkn)                                       \
do {                                                                           \
    const std::string token   (tkn);                                           \
    toml::detail::location<std::string> loc(token);                            \
    const auto result = lxr::invoke(loc);                                      \
    BOOST_CHECK(result.is_err());                                              \
    BOOST_CHECK(loc.begin() == loc.iter());                                    \
} while(false); /**/
<commit_msg>add source_name to test_lex_aux macros<commit_after>#include <iostream>
#include <iomanip>
#include <algorithm>
#include <toml/region.hpp>
#include <toml/result.hpp>

#define TOML11_TEST_LEX_ACCEPT(lxr, tkn, expct)                                \
do {                                                                           \
    const std::string token   (tkn);                                           \
    const std::string expected(expct);                                         \
    toml::detail::location<std::string> loc("test", token);                    \
    const auto result = lxr::invoke(loc);                                      \
    BOOST_CHECK(result.is_ok());                                               \
    if(result.is_ok()){                                                        \
    const auto region = result.unwrap();                                       \
    BOOST_CHECK_EQUAL(region.str(), expected);                                 \
    BOOST_CHECK_EQUAL(region.str().size(), expected.size());                   \
    BOOST_CHECK_EQUAL(std::distance(loc.begin(), loc.iter()), region.size());  \
    } else {                                                                   \
    std::cerr << "lexer " << lxr::pattern() << " failed with input `";         \
    std::cerr << token << "`. expected `" << expected << "`\n";                \
    std::cerr << "reason: " << result.unwrap_err() << '\n';                    \
    }                                                                          \
} while(false);                                                                \
/**/

#define TOML11_TEST_LEX_REJECT(lxr, tkn)                                       \
do {                                                                           \
    const std::string token   (tkn);                                           \
    toml::detail::location<std::string> loc("test", token);                    \
    const auto result = lxr::invoke(loc);                                      \
    BOOST_CHECK(result.is_err());                                              \
    BOOST_CHECK(loc.begin() == loc.iter());                                    \
} while(false); /**/
<|endoftext|>