Fixing entrypoint to be executable

Fixing entrypoint to be executable

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
    now_str=now_str,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
))@
MAINTAINER Dirk Thomas dthomas+buildfarm@@osrfoundation.org

# setup environment
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
@[if 'packages' in locals()]@
@[if packages]@

# install packages
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(packages))@  \
    && rm -rf /var/lib/apt/lists/*

@[end if]@
@[end if]@

# setup keys
RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116

# setup sources.list
RUN echo "deb http://packages.ros.org/ros/@os_name @os_code_name main" > /etc/apt/sources.list.d/ros-latest.list

# install bootstrap tools
ENV ROS_DISTRO @rosdistro_name
RUN apt-get update && apt-get install --no-install-recommends -y \
    python-rosdep \
    python-rosinstall \
    python-vcstools \
    && rm -rf /var/lib/apt/lists/*

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

# install ros packages
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(ros_packages))@  \
    && rm -rf /var/lib/apt/lists/*

# setup entrypoint
COPY ./ros_entrypoint.sh /
RUN chmod +x ./ros_entrypoint.sh

ENTRYPOINT ["/ros_entrypoint.sh"]
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
    now_str=now_str,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
))@
MAINTAINER Dirk Thomas dthomas+buildfarm@@osrfoundation.org

# setup environment
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
@[if 'packages' in locals()]@
@[if packages]@

# install packages
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(packages))@  \
    && rm -rf /var/lib/apt/lists/*

@[end if]@
@[end if]@

# setup keys
RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116

# setup sources.list
RUN echo "deb http://packages.ros.org/ros/@os_name @os_code_name main" > /etc/apt/sources.list.d/ros-latest.list

# install bootstrap tools
ENV ROS_DISTRO @rosdistro_name
RUN apt-get update && apt-get install --no-install-recommends -y \
    python-rosdep \
    python-rosinstall \
    python-vcstools \
    && rm -rf /var/lib/apt/lists/*

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

# install ros packages
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(ros_packages))@  \
    && rm -rf /var/lib/apt/lists/*

# setup entrypoint
COPY ./ros_entrypoint.sh /
ENTRYPOINT ["/ros_entrypoint.sh"]
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]


Rename stringstream in macros to a more unique name (#1862)

* Rename stringstream in macros to a more unique name

Signed-off-by: Doug Smith <079364f7071cf3182f60cd307cf417206dff215e@swri.org>

// generated from rclcpp/resource/logging.hpp.em

// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// 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.

#ifndef RCLCPP__LOGGING_HPP_
#define RCLCPP__LOGGING_HPP_

#include <sstream>
#include <type_traits>

#include "rclcpp/logger.hpp"
#include "rcutils/logging_macros.h"
#include "rclcpp/utilities.hpp"

// These are used for compiling out logging macros lower than a minimum severity.
#define RCLCPP_LOG_MIN_SEVERITY_DEBUG 0
#define RCLCPP_LOG_MIN_SEVERITY_INFO 1
#define RCLCPP_LOG_MIN_SEVERITY_WARN 2
#define RCLCPP_LOG_MIN_SEVERITY_ERROR 3
#define RCLCPP_LOG_MIN_SEVERITY_FATAL 4
#define RCLCPP_LOG_MIN_SEVERITY_NONE 5

#define RCLCPP_FIRST_ARG(N, ...) N
#define RCLCPP_ALL_BUT_FIRST_ARGS(N, ...) __VA_ARGS__

/**
 * \def RCLCPP_LOG_MIN_SEVERITY
 * Define RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
 * in your build options to compile out anything below that severity.
 * Use RCLCPP_LOG_MIN_SEVERITY_NONE to compile out all macros.
 */
#ifndef RCLCPP_LOG_MIN_SEVERITY
#define RCLCPP_LOG_MIN_SEVERITY RCLCPP_LOG_MIN_SEVERITY_DEBUG
#endif

@{
from collections import OrderedDict
from copy import deepcopy
from rcutils.logging import feature_combinations
from rcutils.logging import get_suffix_from_features
from rcutils.logging import severities
from rcutils.logging import throttle_args
from rcutils.logging import throttle_params

throttle_args['condition_before'] = 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(clock, duration)'
del throttle_params['get_time_point_value']
throttle_params['clock'] = 'rclcpp::Clock that will be used to get the time point.'
throttle_params.move_to_end('clock', last=False)

rclcpp_feature_combinations = OrderedDict()
for combinations, feature in feature_combinations.items():
    # skip feature combinations using 'named'
    if 'named' in combinations:
        continue
    rclcpp_feature_combinations[combinations] = feature
# add a stream variant for each available feature combination
stream_arg = 'stream_arg'
for combinations, feature in list(rclcpp_feature_combinations.items()):
    combinations = ('stream', ) + combinations
    feature = deepcopy(feature)
    feature.params[stream_arg] = 'The argument << into a stringstream'
    rclcpp_feature_combinations[combinations] = feature

def get_rclcpp_suffix_from_features(features):
    suffix = get_suffix_from_features(features)
    if 'stream' in features:
        suffix = '_STREAM' + suffix
    return suffix
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
 */
///@@{
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_@(severity)@(suffix)(...)
@[ end for]@

#else
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
// The RCLCPP_@(severity)@(suffix) macro is surrounded by do { .. } while (0)
// to implement the standard C macro idiom to make the macro safe in all
// contexts; see http://c-faq.com/cpp/multistmt.html for more information.
/**
 * \def RCLCPP_@(severity)@(suffix)
 * Log a message with severity @(severity)@
@[ if rclcpp_feature_combinations[feature_combination].doc_lines]@
 with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in rclcpp_feature_combinations[feature_combination].doc_lines]@
 * @(doc_line)
@[ end for]@
 * \param logger The `rclcpp::Logger` to use
@[ for param_name, doc_line in rclcpp_feature_combinations[feature_combination].params.items()]@
 * \param @(param_name) @(doc_line)
@[ end for]@
@[ if 'stream' not in feature_combination]@
 * \param ... The format string, followed by the variable arguments for the format string.
 * It also accepts a single argument of type std::string.
@[ end if]@
 */
@{params = rclcpp_feature_combinations[feature_combination].params.keys()}@
#define RCLCPP_@(severity)@(suffix)(logger@(''.join([', ' + p for p in params]))@
@[ if 'stream' not in feature_combination]@
, ...@
@[ end if]@
) \
  do { \
    static_assert( \
      ::std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(logger)>::type>::type, \
      typename ::rclcpp::Logger>::value, \
      "First argument to logging macros must be an rclcpp::Logger"); \
@[ if 'throttle' in feature_combination]@ \
    auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
      try { \
        *time_point = c.now().nanoseconds(); \
      } catch (...) { \
        RCUTILS_SAFE_FWRITE_TO_STDERR( \
        "[rclcpp|logging.hpp] RCLCPP_@(severity)@(suffix) could not get current time stamp\n"); \
        return RCUTILS_RET_ERROR; \
      } \
        return RCUTILS_RET_OK; \
    }; \
@[ end if] \
@[ if 'stream' in feature_combination]@
    std::stringstream rclcpp_stream_ss_; \
    rclcpp_stream_ss_ << @(stream_arg); \
@[ end if]@
    RCUTILS_LOG_@(severity)@(get_suffix_from_features(feature_combination))_NAMED( \
@{params = ['get_time_point' if p == 'clock' and 'throttle' in feature_combination else p for p in params]}@
@[ if params]@
@(''.join(['      ' + p + ', \\\n' for p in params if p != stream_arg]))@
@[ end if]@
      (logger).get_name(), \
@[ if 'stream' not in feature_combination]@
      __VA_ARGS__); \
@[ else]@
      "%s", rclcpp_stream_ss_.str().c_str()); \
@[ end if]@
  } while (0)

@[ end for]@
#endif
///@@}

@[end for]@

#endif  // RCLCPP__LOGGING_HPP_

// generated from rclcpp/resource/logging.hpp.em

// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// 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.

#ifndef RCLCPP__LOGGING_HPP_
#define RCLCPP__LOGGING_HPP_

#include <sstream>
#include <type_traits>

#include "rclcpp/logger.hpp"
#include "rcutils/logging_macros.h"
#include "rclcpp/utilities.hpp"

// These are used for compiling out logging macros lower than a minimum severity.
#define RCLCPP_LOG_MIN_SEVERITY_DEBUG 0
#define RCLCPP_LOG_MIN_SEVERITY_INFO 1
#define RCLCPP_LOG_MIN_SEVERITY_WARN 2
#define RCLCPP_LOG_MIN_SEVERITY_ERROR 3
#define RCLCPP_LOG_MIN_SEVERITY_FATAL 4
#define RCLCPP_LOG_MIN_SEVERITY_NONE 5

#define RCLCPP_FIRST_ARG(N, ...) N
#define RCLCPP_ALL_BUT_FIRST_ARGS(N, ...) __VA_ARGS__

/**
 * \def RCLCPP_LOG_MIN_SEVERITY
 * Define RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
 * in your build options to compile out anything below that severity.
 * Use RCLCPP_LOG_MIN_SEVERITY_NONE to compile out all macros.
 */
#ifndef RCLCPP_LOG_MIN_SEVERITY
#define RCLCPP_LOG_MIN_SEVERITY RCLCPP_LOG_MIN_SEVERITY_DEBUG
#endif

@{
from collections import OrderedDict
from copy import deepcopy
from rcutils.logging import feature_combinations
from rcutils.logging import get_suffix_from_features
from rcutils.logging import severities
from rcutils.logging import throttle_args
from rcutils.logging import throttle_params

throttle_args['condition_before'] = 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(clock, duration)'
del throttle_params['get_time_point_value']
throttle_params['clock'] = 'rclcpp::Clock that will be used to get the time point.'
throttle_params.move_to_end('clock', last=False)

rclcpp_feature_combinations = OrderedDict()
for combinations, feature in feature_combinations.items():
    # skip feature combinations using 'named'
    if 'named' in combinations:
        continue
    rclcpp_feature_combinations[combinations] = feature
# add a stream variant for each available feature combination
stream_arg = 'stream_arg'
for combinations, feature in list(rclcpp_feature_combinations.items()):
    combinations = ('stream', ) + combinations
    feature = deepcopy(feature)
    feature.params[stream_arg] = 'The argument << into a stringstream'
    rclcpp_feature_combinations[combinations] = feature

def get_rclcpp_suffix_from_features(features):
    suffix = get_suffix_from_features(features)
    if 'stream' in features:
        suffix = '_STREAM' + suffix
    return suffix
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
 */
///@@{
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_@(severity)@(suffix)(...)
@[ end for]@

#else
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
// The RCLCPP_@(severity)@(suffix) macro is surrounded by do { .. } while (0)
// to implement the standard C macro idiom to make the macro safe in all
// contexts; see http://c-faq.com/cpp/multistmt.html for more information.
/**
 * \def RCLCPP_@(severity)@(suffix)
 * Log a message with severity @(severity)@
@[ if rclcpp_feature_combinations[feature_combination].doc_lines]@
 with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in rclcpp_feature_combinations[feature_combination].doc_lines]@
 * @(doc_line)
@[ end for]@
 * \param logger The `rclcpp::Logger` to use
@[ for param_name, doc_line in rclcpp_feature_combinations[feature_combination].params.items()]@
 * \param @(param_name) @(doc_line)
@[ end for]@
@[ if 'stream' not in feature_combination]@
 * \param ... The format string, followed by the variable arguments for the format string.
 * It also accepts a single argument of type std::string.
@[ end if]@
 */
@{params = rclcpp_feature_combinations[feature_combination].params.keys()}@
#define RCLCPP_@(severity)@(suffix)(logger@(''.join([', ' + p for p in params]))@
@[ if 'stream' not in feature_combination]@
, ...@
@[ end if]@
) \
  do { \
    static_assert( \
      ::std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(logger)>::type>::type, \
      typename ::rclcpp::Logger>::value, \
      "First argument to logging macros must be an rclcpp::Logger"); \
@[ if 'throttle' in feature_combination]@ \
    auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
      try { \
        *time_point = c.now().nanoseconds(); \
      } catch (...) { \
        RCUTILS_SAFE_FWRITE_TO_STDERR( \
        "[rclcpp|logging.hpp] RCLCPP_@(severity)@(suffix) could not get current time stamp\n"); \
        return RCUTILS_RET_ERROR; \
      } \
        return RCUTILS_RET_OK; \
    }; \
@[ end if] \
@[ if 'stream' in feature_combination]@
    std::stringstream ss; \
    ss << @(stream_arg); \
@[ end if]@
    RCUTILS_LOG_@(severity)@(get_suffix_from_features(feature_combination))_NAMED( \
@{params = ['get_time_point' if p == 'clock' and 'throttle' in feature_combination else p for p in params]}@
@[ if params]@
@(''.join(['      ' + p + ', \\\n' for p in params if p != stream_arg]))@
@[ end if]@
      (logger).get_name(), \
@[ if 'stream' not in feature_combination]@
      __VA_ARGS__); \
@[ else]@
      "%s", ss.str().c_str()); \
@[ end if]@
  } while (0)

@[ end for]@
#endif
///@@}

@[end for]@

#endif  // RCLCPP__LOGGING_HPP_


Mimic the behaviour and style of the sugar GTK+ 2 sub-toolbars in GTK+ 3

This patch takes care of the background color of the toolbutton
when pressed and hovered. Furthermore the border radius for the
highlightning when pressed is defined. There is a follow up patch
in sugar-toolkit-gtk3 to make use of this.

Signed-off-by: Gonzalo Odiard <8e43bec6c9a4aba7dc358247a21ab52d301a2840@laptop.org>
Signed-off-by: Manuel Quiñones <6f5069c5b6be23302a13accec56587944be09079@laptop.org>
Acked-by: Simon Schampijer <088e16a1019277b15d58faf0541e11910eb756f6@laptop.org>

${
import math

def my_floor(num):
    return int(math.floor(num))

def my_ceil(num):
    return int(math.ceil(num))

# Should we set the line width in the engine to 2.25, and draw non pixel aligned lines?
# Are these already the correct sizes for the XO?

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 9
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 6      # 7.68

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26
scale_slider_width = my_floor(2 * subcell_size + line_width)
thickness = my_ceil(line_width)

icon_small = icon_base * 3
icon_large = icon_base * 5

}

* {
    -sugar-focus-line: @white;

    background-color: @button_grey;
    color: @black;

    border-color: transparent;
    border-radius: 0px;
    border-style: none;

    padding: $(thickness)px;

    /* A lot of these will probably need to be changed, but this has to
       be done when the exact sizes are known */
    -GtkWidget-interior-focus: 0;

    /* we have to disable focus border for GtkTreeView, see #1261 */
    -GtkTreeView-interior-focus: 1;
    -GtkTreeView-focus-line-width: 0;

    -GtkTextView-interior-focus: 1;

    -GtkWidget-focus-line-width: 0;  /* Prevents some drawing glitches */
    -GtkEntry-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkWidget-focus-padding: 3;
    /* 0.05 works good for both the sugar and sugar-xo themes */
    -GtkWidget-cursor-aspect-ratio: 0.05;

    -GtkWidget-wide-separators: false;
    -GtkWidget-separator-height: $thickness;
    -GtkWidget-separator-width: $thickness;

    -GtkWidget-scroll-arrow-hlength: $subcell_size;
    -GtkWidget-scroll-arrow-vlength: $subcell_size;

    -GtkRange-activate-slider: 1;

    /* We fake the default border in the theme */
    -GtkButton-default-border: 0 0 0 0;
    -GtkButton-default-outside-border: 0 0 0 0;
    -GtkButton-image-spacing: $subcell_size;

    -GtkEntry-progress-border: $thickness $thickness $thickness $thickness;

    -GtkScrolledWindow-scrollbar-spacing: 0;

    -GtkExpander-expander-size: $font_height;
    -GtkExpander-expander-spacing: 2;

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

    -GtkToolbar-space-size: $(2*subcell_size);

    -GtkProgressBar-min-horizontal-bar-height: $subcell_size;
    -GtkProgressBar-min-vertical-bar-width: $subcell_size;

    -GtkButtonBox-child-min-height: $(3*subcell_size);
    -GtkButtonBox-child-min-width: $(3*subcell_size);
    -GtkButtonBox-child-internal-pad-x: 0;
    -GtkButtonBox-child-internal-pad-y: 0;

    -GtkCheckButton-indicator-size: $radio_size;
    -GtkCheckButton-indicator-spacing: 3;
}

*:active {
    background-color: @white;
    color: @black;
}

/* Backgrounds and windows */

.background {
    padding: 0px;
    border-width: 0px;
    background-color: @panel_grey;
}

.window {
    background-color: @panel_grey;
    color: @black;
}

/* Handle this differently? */
.window *:insensitive {
    background-color: @panel_grey;
}

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    /* It would be nicer to just set the inner-border, but that does not work
       for combo boxes ... The combobox ignores it, so set it to 0px
       See http://bugzilla.gnome.org/show_bug.cgi?id=485762 */

    -GtkButton-inner-border: 0 0 0 0;
    padding: $(border)px $(border)px $(border)px $(border)px;

    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

.button * {
    color: @white;
}

.button:focused {
    border-width: $(thickness)px;
    border-color: @white;
    border-style: solid;
}

/* Spin buttons */

${ spin_ythickness = my_ceil(3*subcell_size - font_height) }
${ spin_xthickness = subcell_size }
${ spin_btn_ythickness = spin_ythickness }
${ spin_btn_xthickness = subcell_size * 2 }

.spinbutton.button,
.spinbutton.button:focused,
.spinbutton.button:active {
    padding: $(spin_ythickness)px $(spin_xthickness)px;
    border-color: @button_grey;
    border-width: $(thickness)px;
    background-color: @button_grey;
    color: @white;
}

.spinbutton.button:active {
    background-color: @white;
    color: @black;
    border-color: @white;
}

.spinbutton.button:insensitive {
    background-color: @selection_grey;
}

.spinbutton.button:focused,
.spinbutton.button:focused:prelight,
.spinbutton.button:focused:insensitive {
    border-color: @white;
}

/* Toggle buttons */

GtkToggleButton.button:active {
    background-color: @white;
}

GtkToggleButton.button:active GtkLabel {
    color: @black;
}

/* Entries and views */

.view {
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background-color: @white;
}

.entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @selection_grey;
    border-style: solid;
    background-color: @text_field_grey;
    color: @black;
    ${ entry_ythickness = my_ceil(0.2 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    ${ entry_xthickness = my_ceil(0.32 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    padding: $(2 * max(my_ceil((3*subcell_size - font_height - entry_ythickness*2)/2.0),0))px $(2 * max(subcell_size - entry_xthickness, 0))px $(2 * max(my_floor((3*subcell_size - font_height - entry_ythickness*2)/2.0), 0))px $(2 * max(subcell_size - entry_xthickness, 0))px;
}

.entry.progressbar {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    background-color: @selection_grey;
}

.entry:focused {
    background-color: @white;
}

.entry:insensitive, .view:insensitive {
    background-color: @button_grey;
}

.view:selected {
    background-color: @panel_grey;
    color: @black;
}

.entry:selected, .entry:selected:focused,
.view:selected:focused {
    background-color: @selection_grey;
    color: @black;
}

/* Combo boxes */

GtkComboBox * {
    color: @white;
}

GtkComboBox .separator {
    /* Remove the separator turning it transparent */
    color: alpha(@theme_base_color, 0.0);
}

.toolbar GtkToggleButton.button:active,
SugarPaletteWindowWidget GtkToggleButton.button:active {
    background-color: @button_grey;
}

/* Notebooks */

.notebook {
    background-color: @selection_grey;
    border-width: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
}

.notebook tab,
.notebook tab GtkLabel {
    background-color: @button_grey;
    color: @white;
}

.notebook tab:active {
    background-color: @selection_grey;
}

/* Browser notebook */

BrowserNotebook.notebook tab {
    background-color: @selection_grey;
}

BrowserNotebook.notebook tab .button {
    border-radius: $(toolbutton_padding)px;
}

BrowserNotebook.notebook tab:active {
    background-color: @toolbar_grey;
}

BrowserNotebook.notebook tab:active *:active {
    color: @white;
}

/* Control panel */

SugarSectionView {
    background-color: @white;
    color: @black;
}

SugarSectionView *:insensitive {
    background-color: @white;
}

/* Alert */

SugarAlert {
    background-color: @black;
    color: @white;
}

SugarAlert *:insensitive {
    background-color: @black;
}

/* Tray */

SugarHTray * , SugarVTray * { background-color: @toolbar_grey;}

/* Menus and palettes */

SugarPaletteWindowWidget.background {
    background-color: @black;
}

SugarPaletteWindowWidget GtkLabel {
    color: @white;
}

SugarPaletteMenuWidget.background {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

SugarPaletteWindow SugarGroupBox {
    background-color: @toolbar_grey;
    color: @white;
}

SugarPaletteWindow SugarGroupBox *:insensitive {
    background-color: @toolbar_grey;
}

.menu,
.palette {
    background-color: @black;
    color: @white;

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*thickness));
    -GtkMenu-horizontal-padding: $thickness;
    -GtkMenu-vertical-padding  : $thickness;
    /* This means the outline of the submenu overlaps with a palette.
     * However in the case of two normal menus, they are next to each other.
     * It is not possible to be smarter about this, because the style comes from
     * the submenu. */
    -GtkMenu-horizontal-offset : 0;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px;
    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

.menu :prelight, palette :prelight {
    color: @white
}

.menu :active, palette :active {
    background-color: @button_grey;
}

.palette {
    padding: $(thickness)px;
}

.palette .menu {
    -GtkMenu-horizontal-padding: 0;
    -GtkMenu-vertical-padding: 0;

    padding: 0px $(subcell_size)px;
}

.menu * {
    color: @white;
}

GtkMenuItem {
    padding: $(subcell_size)px $((subcell_size * 3 - font_height) / 2)px;
}

/* Scrollbars */

.scrollbar {
    -GtkRange-slider-width: $subcell_size;
    -GtkRange-trough-border: 0;
    -GtkRange-stepper-size: 0;
    -GtkScrollbar-min-slider-length: $(3*subcell_size);
    -GtkScrollbar-has-forward-stepper: 0;
    -GtkScrollbar-has-backward-stepper: 0;
    -GtkScrollbar-has-secondary-forward-stepper: 0;
    -GtkScrollbar-has-secondary-backward-stepper: 0;
}

.scrollbar.trough {
    background-color: @button_grey;
    border-width: 0px;
}

.scrollbar.slider {
    background-color: @white;
    border-radius: $(2*subcell_size)px;
    border-width: 0px;
}

.scrollbar.slider:active {
    background-color: @text_field_grey;
}

/* Progress bars */

/* Scrollbar padding hack? What was that about? */

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @white;
    border-radius: 10px;
    border-style: solid;
    border-width: 0px;
}

GtkProgressBar.trough {
    background-color: alpha (@black, 0.0);
    border-style: solid;
    border-radius: 10px;
    border-color: @button_grey;
    border-width: 2px;
}

/* Separators */

GtkVSeparator, GtkHSeparator,
.toolbar GtkSeparatorToolItem {
    color: @button_grey;
}

/* Tool buttons */

.toolbar {
padding: 0px;
}

.toolbar GtkToolButton .button,
SugarPaletteWindowWidget GtkToolButton .button {
    border-radius: $(toolbutton_padding)px;
    padding: $(default_padding)px;
}

.toolbar GtkToolButton .button:prelight,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    background-color: @black;
    border-radius: 0px;
    border-width: 0px;
}

.toolbar GtkToolButton .button:active,
SugarPaletteWindowWidget GtkToolButton .button:active {
    background-color: @black;
    border-radius: 0px;
}

.toolbar GtkToolButton .button:active:prelight,
SugarPaletteWindowWidget GtkToolButton .button:active:prelight {
    background-color: @button_grey;
    border-radius: $(subcell_size)px;
    border-width: $(default_padding)px;
    border-color: transparent;
}

/* Scales */

GtkScale {
    -GtkScale-slider-length: $scale_slider_width;
    -GtkRange-slider-width: $scale_slider_width;
}

GtkScale.trough {
    border-style: solid;
    border-radius: 30px;
    border-color: @button_grey;
    border-width: 2px;
}

GtkScale.trough:focused {
    border-color: @white;
}

GtkScale.trough.top, GtkScale.trough.left {
    background-color: @white;
}

GtkScale.slider {
    color: alpha(@theme_base_color, 0.0);
    background-color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider.svg");
}

GtkScale.slider:active {
    color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton:prelight {
    background-color: alpha(@theme_base_color, 0.0);
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.radio:selected,
.radio:selected row:selected,
.radio:selected row:selected:focused {
    background-image: url("assets/radio-selected.svg");
}

.radio:active,
.radio row:selected:active,
.radio row:selected:focused:active {
    background-image: url("assets/radio-active.svg");
}

.radio:active:selected,
.radio:selected row:selected:active,
.radio:selected row:selected:focused:active {
    background-image: url("assets/radio-active-selected.svg");
}

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.check:selected,
.check:selected row:selected,
.check:selected row:selected:focused {
    background-image: url("assets/checkbox-unchecked-selected.svg");
}

.check:active,
.check row:selected:active,
.check row:selected:focused:active {
    background-image: url("assets/checkbox-checked.svg");
}

.check:active:selected,
.check:selected row:selected:active,
.check:selected row:selected:focused:active {
    background-image: url("assets/checkbox-checked-selected.svg");
}

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}

${
import math

def my_floor(num):
    return int(math.floor(num))

def my_ceil(num):
    return int(math.ceil(num))

# Should we set the line width in the engine to 2.25, and draw non pixel aligned lines?
# Are these already the correct sizes for the XO?

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 9
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 6      # 7.68

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26
scale_slider_width = my_floor(2 * subcell_size + line_width)
thickness = my_ceil(line_width)

icon_small = icon_base * 3
icon_large = icon_base * 5

}

* {
    -sugar-focus-line: @white;

    background-color: @button_grey;
    color: @black;

    border-color: transparent;
    border-radius: 0px;
    border-style: none;

    padding: $(thickness)px;

    /* A lot of these will probably need to be changed, but this has to
       be done when the exact sizes are known */
    -GtkWidget-interior-focus: 0;

    /* we have to disable focus border for GtkTreeView, see #1261 */
    -GtkTreeView-interior-focus: 1;
    -GtkTreeView-focus-line-width: 0;

    -GtkTextView-interior-focus: 1;

    -GtkWidget-focus-line-width: 0;  /* Prevents some drawing glitches */
    -GtkEntry-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkWidget-focus-padding: 3;
    /* 0.05 works good for both the sugar and sugar-xo themes */
    -GtkWidget-cursor-aspect-ratio: 0.05;

    -GtkWidget-wide-separators: false;
    -GtkWidget-separator-height: $thickness;
    -GtkWidget-separator-width: $thickness;

    -GtkWidget-scroll-arrow-hlength: $subcell_size;
    -GtkWidget-scroll-arrow-vlength: $subcell_size;

    -GtkRange-activate-slider: 1;

    /* We fake the default border in the theme */
    -GtkButton-default-border: 0 0 0 0;
    -GtkButton-default-outside-border: 0 0 0 0;
    -GtkButton-image-spacing: $subcell_size;

    -GtkEntry-progress-border: $thickness $thickness $thickness $thickness;

    -GtkScrolledWindow-scrollbar-spacing: 0;

    -GtkExpander-expander-size: $font_height;
    -GtkExpander-expander-spacing: 2;

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

    -GtkToolbar-space-size: $(2*subcell_size);

    -GtkProgressBar-min-horizontal-bar-height: $subcell_size;
    -GtkProgressBar-min-vertical-bar-width: $subcell_size;

    -GtkButtonBox-child-min-height: $(3*subcell_size);
    -GtkButtonBox-child-min-width: $(3*subcell_size);
    -GtkButtonBox-child-internal-pad-x: 0;
    -GtkButtonBox-child-internal-pad-y: 0;

    -GtkCheckButton-indicator-size: $radio_size;
    -GtkCheckButton-indicator-spacing: 3;
}

*:active {
    background-color: @white;
    color: @black;
}

/* Backgrounds and windows */

.background {
    padding: 0px;
    border-width: 0px;
    background-color: @panel_grey;
}

.window {
    background-color: @panel_grey;
    color: @black;
}

/* Handle this differently? */
.window *:insensitive {
    background-color: @panel_grey;
}

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    /* It would be nicer to just set the inner-border, but that does not work
       for combo boxes ... The combobox ignores it, so set it to 0px
       See http://bugzilla.gnome.org/show_bug.cgi?id=485762 */

    -GtkButton-inner-border: 0 0 0 0;
    padding: $(border)px $(border)px $(border)px $(border)px;

    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

.button * {
    color: @white;
}

.button:focused {
    border-width: $(thickness)px;
    border-color: @white;
    border-style: solid;
}

/* Spin buttons */

${ spin_ythickness = my_ceil(3*subcell_size - font_height) }
${ spin_xthickness = subcell_size }
${ spin_btn_ythickness = spin_ythickness }
${ spin_btn_xthickness = subcell_size * 2 }

.spinbutton.button,
.spinbutton.button:focused,
.spinbutton.button:active {
    padding: $(spin_ythickness)px $(spin_xthickness)px;
    border-color: @button_grey;
    border-width: $(thickness)px;
    background-color: @button_grey;
    color: @white;
}

.spinbutton.button:active {
    background-color: @white;
    color: @black;
    border-color: @white;
}

.spinbutton.button:insensitive {
    background-color: @selection_grey;
}

.spinbutton.button:focused,
.spinbutton.button:focused:prelight,
.spinbutton.button:focused:insensitive {
    border-color: @white;
}

/* Toggle buttons */

GtkToggleButton.button:active {
    background-color: @white;
}

GtkToggleButton.button:active GtkLabel {
    color: @black;
}

/* Entries and views */

.view {
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background-color: @white;
}

.entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @selection_grey;
    border-style: solid;
    background-color: @text_field_grey;
    color: @black;
    ${ entry_ythickness = my_ceil(0.2 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    ${ entry_xthickness = my_ceil(0.32 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    padding: $(2 * max(my_ceil((3*subcell_size - font_height - entry_ythickness*2)/2.0),0))px $(2 * max(subcell_size - entry_xthickness, 0))px $(2 * max(my_floor((3*subcell_size - font_height - entry_ythickness*2)/2.0), 0))px $(2 * max(subcell_size - entry_xthickness, 0))px;
}

.entry.progressbar {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    background-color: @selection_grey;
}

.entry:focused {
    background-color: @white;
}

.entry:insensitive, .view:insensitive {
    background-color: @button_grey;
}

.view:selected {
    background-color: @panel_grey;
    color: @black;
}

.entry:selected, .entry:selected:focused,
.view:selected:focused {
    background-color: @selection_grey;
    color: @black;
}

/* Combo boxes */

GtkComboBox * {
    color: @white;
}

GtkComboBox .separator {
    /* Remove the separator turning it transparent */
    color: alpha(@theme_base_color, 0.0);
}

.toolbar GtkToggleButton.button:active,
SugarPaletteWindowWidget GtkToggleButton.button:active {
    background-color: @button_grey;
}

/* Notebooks */

.notebook {
    background-color: @selection_grey;
    border-width: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
}

.notebook tab,
.notebook tab GtkLabel {
    background-color: @button_grey;
    color: @white;
}

.notebook tab:active {
    background-color: @selection_grey;
}

/* Browser notebook */

BrowserNotebook.notebook tab {
    background-color: @selection_grey;
}

BrowserNotebook.notebook tab .button {
    border-radius: $(toolbutton_padding)px;
}

BrowserNotebook.notebook tab:active {
    background-color: @toolbar_grey;
}

BrowserNotebook.notebook tab:active *:active {
    color: @white;
}

/* Control panel */

SugarSectionView {
    background-color: @white;
    color: @black;
}

SugarSectionView *:insensitive {
    background-color: @white;
}

/* Alert */

SugarAlert {
    background-color: @black;
    color: @white;
}

SugarAlert *:insensitive {
    background-color: @black;
}

/* Tray */

SugarHTray * , SugarVTray * { background-color: @toolbar_grey;}

/* Menus and palettes */

SugarPaletteWindowWidget.background {
    background-color: @black;
}

SugarPaletteWindowWidget GtkLabel {
    color: @white;
}

SugarPaletteMenuWidget.background {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

SugarPaletteWindow SugarGroupBox {
    background-color: @toolbar_grey;
    color: @white;
}

SugarPaletteWindow SugarGroupBox *:insensitive {
    background-color: @toolbar_grey;
}

.menu,
.palette {
    background-color: @black;
    color: @white;

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*thickness));
    -GtkMenu-horizontal-padding: $thickness;
    -GtkMenu-vertical-padding  : $thickness;
    /* This means the outline of the submenu overlaps with a palette.
     * However in the case of two normal menus, they are next to each other.
     * It is not possible to be smarter about this, because the style comes from
     * the submenu. */
    -GtkMenu-horizontal-offset : 0;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px;
    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

.menu :prelight, palette :prelight {
    color: @white
}

.menu :active, palette :active {
    background-color: @button_grey;
}

.palette {
    padding: $(thickness)px;
}

.palette .menu {
    -GtkMenu-horizontal-padding: 0;
    -GtkMenu-vertical-padding: 0;

    padding: 0px $(subcell_size)px;
}

.menu * {
    color: @white;
}

GtkMenuItem {
    padding: $(subcell_size)px $((subcell_size * 3 - font_height) / 2)px;
}

/* Scrollbars */

.scrollbar {
    -GtkRange-slider-width: $subcell_size;
    -GtkRange-trough-border: 0;
    -GtkRange-stepper-size: 0;
    -GtkScrollbar-min-slider-length: $(3*subcell_size);
    -GtkScrollbar-has-forward-stepper: 0;
    -GtkScrollbar-has-backward-stepper: 0;
    -GtkScrollbar-has-secondary-forward-stepper: 0;
    -GtkScrollbar-has-secondary-backward-stepper: 0;
}

.scrollbar.trough {
    background-color: @button_grey;
    border-width: 0px;
}

.scrollbar.slider {
    background-color: @white;
    border-radius: $(2*subcell_size)px;
    border-width: 0px;
}

.scrollbar.slider:active {
    background-color: @text_field_grey;
}

/* Progress bars */

/* Scrollbar padding hack? What was that about? */

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @white;
    border-radius: 10px;
    border-style: solid;
    border-width: 0px;
}

GtkProgressBar.trough {
    background-color: alpha (@black, 0.0);
    border-style: solid;
    border-radius: 10px;
    border-color: @button_grey;
    border-width: 2px;
}

/* Separators */

GtkVSeparator, GtkHSeparator,
.toolbar GtkSeparatorToolItem {
    color: @button_grey;
}

/* Tool buttons */

.toolbar GtkToolButton .button,
SugarPaletteWindowWidget GtkToolButton .button {
    border-radius: $(toolbutton_padding)px;
}

.toolbar GtkToolButton .button:prelight,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    background-color: @black;
}

.toolbar GtkToolButton .button:active:prelight,
SugarPaletteWindowWidget GtkToolButton .button:active:prelight {
    background-color: @button_grey;
}

/* Scales */

GtkScale {
    -GtkScale-slider-length: $scale_slider_width;
    -GtkRange-slider-width: $scale_slider_width;
}

GtkScale.trough {
    border-style: solid;
    border-radius: 30px;
    border-color: @button_grey;
    border-width: 2px;
}

GtkScale.trough:focused {
    border-color: @white;
}

GtkScale.trough.top, GtkScale.trough.left {
    background-color: @white;
}

GtkScale.slider {
    color: alpha(@theme_base_color, 0.0);
    background-color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider.svg");
}

GtkScale.slider:active {
    color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton:prelight {
    background-color: alpha(@theme_base_color, 0.0);
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.radio:selected,
.radio:selected row:selected,
.radio:selected row:selected:focused {
    background-image: url("assets/radio-selected.svg");
}

.radio:active,
.radio row:selected:active,
.radio row:selected:focused:active {
    background-image: url("assets/radio-active.svg");
}

.radio:active:selected,
.radio:selected row:selected:active,
.radio:selected row:selected:focused:active {
    background-image: url("assets/radio-active-selected.svg");
}

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.check:selected,
.check:selected row:selected,
.check:selected row:selected:focused {
    background-image: url("assets/checkbox-unchecked-selected.svg");
}

.check:active,
.check row:selected:active,
.check row:selected:focused:active {
    background-image: url("assets/checkbox-checked.svg");
}

.check:active:selected,
.check:selected row:selected:active,
.check:selected row:selected:focused:active {
    background-image: url("assets/checkbox-checked-selected.svg");
}

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}


Allow system Emerson unit tests to trigger success and only record success if a failure hadn't already been encountered.

(function ()
 {
     var UNIT_TEST_HEADER_BEGIN   = 'UNIT_TEST_BEGIN';
     var UNIT_TEST_HEADER_PRINT   = 'UNIT_TEST_PRINT';
     var UNIT_TEST_HEADER_SUCCESS = 'UNIT_TEST_SUCCESS';
     var UNIT_TEST_HEADER_FAILURE = 'UNIT_TEST_FAIL';

     /**
      Should only be called from within util.em
      */
     var __internalPrint = function(testName, header, toPrint)
     {
         system.__debugPrint('\n');
         system.__debugPrint(header + ' testName: ' + testName + ' ' + toPrint);
         system.__debugPrint('\n');
     };

     var __beginTestPrint = function (testName)
     {
         __internalPrint(testName, UNIT_TEST_HEADER_BEGIN, '');
     };

     UnitTest = function (testName)
     {
         this.testName = testName;
         __beginTestPrint(this.testName);
         this.hasFailed = false;
     };

     UnitTest.prototype.print = function (toPrint)
     {
         __internalPrint(this.testName,UNIT_TEST_HEADER_PRINT,toPrint);
     };

     UnitTest.prototype.success = function(toPrint)
     {
         if (!this.hasFailed)
             __internalPrint(this.testName,UNIT_TEST_HEADER_SUCCESS,toPrint);
     };

     UnitTest.prototype.fail = function(toPrint)
     {
         __internalPrint(this.testName,UNIT_TEST_HEADER_FAILURE,toPrint);
         this.hasFailed = true;
     };

 })();

/**
 Waits timeToWait seconds, then executes beforeKillFunc, then kills
 whichever entity called killAfterTime.
 */
function killAfterTime(timeToWait, beforeKillFunc)
{
    var onTimeout = function()
    {
        if (typeof(beforeKillFunc) == 'function')
            beforeKillFunc();

        system.killEntity();
    };
    system.timeout(timeToWait,onTimeout);
}

/**

 @param {object} sucObj An object with string indices and boolean
 values.  If one of the values is false, then we use unitTestObj to
 print a failure message with the index string as the failure message.
 If *all* items in sucObj are true, then prints success message.

 @param {UnitTest object} unitTestObj.  @see UnitTest.

 */
function printSuccessObject(sucObj, unitTestObj)
{
    var success = true;
    for (var s in sucObj)
    {
        if (!sucObj[s])
        {
            success= false;
            unitTestObj.fail(s);
        }
    }
    if (success)
        unitTestObj.success(' succeeded!');
}

(function ()
 {
     var UNIT_TEST_HEADER_BEGIN   = 'UNIT_TEST_BEGIN';
     var UNIT_TEST_HEADER_PRINT   = 'UNIT_TEST_PRINT';
     var UNIT_TEST_HEADER_SUCCESS = 'UNIT_TEST_SUCCESS';
     var UNIT_TEST_HEADER_FAILURE = 'UNIT_TEST_FAIL';

     /**
      Should only be called from within util.em
      */
     var __internalPrint = function(testName, header, toPrint)
     {
         system.__debugPrint('\n');
         system.__debugPrint(header + ' testName: ' + testName + ' ' + toPrint);
         system.__debugPrint('\n');
     };

     var __beginTestPrint = function (testName)
     {
         __internalPrint(testName, UNIT_TEST_HEADER_BEGIN, '');
     };

     UnitTest = function (testName)
     {
         this.testName = testName;
         __beginTestPrint(this.testName);
         this.hasFailed = false;
     };

     UnitTest.prototype.print = function (toPrint)
     {
         __internalPrint(this.testName,UNIT_TEST_HEADER_PRINT,toPrint);
     };

     UnitTest.prototype.success = function(toPrint)
     {
         __internalPrint(this.testName,UNIT_TEST_HEADER_SUCCESS,toPrint);
     };

     UnitTest.prototype.fail = function(toPrint)
     {
         __internalPrint(this.testName,UNIT_TEST_HEADER_FAILURE,toPrint);
         this.hasFailed = true;
     };

 })();

/**
 Waits timeToWait seconds, then executes beforeKillFunc, then kills
 whichever entity called killAfterTime.
 */
function killAfterTime(timeToWait, beforeKillFunc)
{
    var onTimeout = function()
    {
        if (typeof(beforeKillFunc) == 'function')
            beforeKillFunc();

        system.killEntity();
    };
    system.timeout(timeToWait,onTimeout);
}

/**

 @param {object} sucObj An object with string indices and boolean
 values.  If one of the values is false, then we use unitTestObj to
 print a failure message with the index string as the failure message.
 If *all* items in sucObj are true, then prints success message.

 @param {UnitTest object} unitTestObj.  @see UnitTest.

 */
function printSuccessObject(sucObj, unitTestObj)
{
    var success = true;
    for (var s in sucObj)
    {
        if (!sucObj[s])
        {
            success= false;
            unitTestObj.fail(s);
        }
    }
    if (success)
        unitTestObj.success(' succeeded!');
}


array attribute serialization tests

array attribute serialization tests

describe 'Ep.RestSerializer', ->

  beforeEach ->
    @Serializer = Ep.RestSerializer.extend()
    @container = new Ember.Container()
    @container.register('serializer:main', @Serializer)

    @container.register('transform:boolean', Ep.BooleanTransform)
    @container.register('transform:date', Ep.DateTransform)
    @container.register('transform:number', Ep.NumberTransform)
    @container.register('transform:string', Ep.StringTransform)

    @serializer = @container.lookup('serializer:main')

  context 'simple model', ->
    beforeEach ->
      @Post = Ep.Model.extend
        title: Ep.attr('string')
        longTitle: Ep.attr('string')
      @container.register 'model:post', @Post

    describe 'deserializePayload', ->

      it 'reads plural hash key', ->
        data = {posts: {id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'reads singular hash key', ->
        data = {post: {id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'reads array value', ->
        data = {post: [{id: 1, title: 'wat', long_title: 'wat omgawd'}] }
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'obeys custom keys', ->
        @serializer.reopen
          properties:
            title:
              key: 'POST_TITLE'
        data = {post: {id: 1, POST_TITLE: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'reads null client_id as null', ->
        data = {posts: {client_id: null, id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post.clientId).to.be.null

      it 'reads revs', ->
        data = {posts: {rev: 123, client_rev: 321, client_id: 1, id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post.rev).to.eq(123)
        expect(post.clientRev).to.eq(321)

      it 'respects aliases', ->
        @serializer.reopen
          aliases:
            blog_post: 'post'

        data = {blog_post: {id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

    describe 'serialization', ->

      it 'serializes', ->
        post = @Post.create
          id: 1
          clientId: "2"
          title: "wat"
          longTitle: 'wat omgawd'
          rev: 123
          clientRev: 321
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql(client_id: "2", id: 1, title: 'wat', long_title: 'wat omgawd', rev: 123, client_rev: 321)

      it 'handles unloaded lazy model', ->
        lazyPost = Ep.LazyModel.create
          id: 1,
          clientId: "2"
          type: @Post
        data = @serializer.serialize(lazyPost, includeId: true)
        expect(data).to.eql client_id: "2", id: 1

      it 'handles loaded lazy model', ->
        post = @Post.create()
        post.id = 1
        post.clientId = "2"
        post.title = 'wat'
        post.longTitle = 'wat omgawd'
        lazyPost = Ep.LazyModel.create
          id: 1,
          type: @Post
        lazyPost.resolve(post)
        data = @serializer.serialize(lazyPost, includeId: true)
        expect(data).to.eql client_id: "2", id: 1, title: 'wat', long_title: 'wat omgawd'

      it 'obeys custom keys', ->
        @serializer.reopen
          properties:
            title:
              key: 'POST_TITLE'
        post = @Post.create()
        post.id = 1
        post.clientId = "2"
        post.title = 'wat'
        post.longTitle = 'wat omgawd'
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql({client_id: "2", id: 1, POST_TITLE: 'wat', long_title: 'wat omgawd'})

  context 'model with raw and object properties', ->

    beforeEach ->
      @Post = Ep.Model.extend
        title: Ep.attr()
        object: Ep.attr()
      @container.register 'model:post', @Post

    describe 'deserializePayload', ->

      it 'deserializes', ->
        data = {posts: {id: 1, title: 'wat', object: {prop: 'sup'}}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.object.prop).to.eq('sup')
        expect(post.id).to.eq("1")

    describe 'serialization', ->

      it 'serializes object', ->
        post = @Post.create
          id: 1
          clientId: "2"
          title: "wat"
          object: {prop: 'sup'}
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql(client_id: "2", id: 1, title: 'wat', object: {prop: 'sup'})

      it 'serializes array', ->
        post = @Post.create
          id: 1
          clientId: "2"
          title: "wat"
          object: ['asd']
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql(client_id: "2", id: 1, title: 'wat', object: ['asd'])

      it 'serializes empty array', ->
        post = @Post.create
          id: 1
          clientId: "2"
          title: "wat"
          object: []
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql(client_id: "2", id: 1, title: 'wat', object: [])

      it 'serializes complex object', ->
        post = @Post.create
          id: 1
          clientId: "2"
          title: "wat"
          object: {tags: ['ruby', 'java']}
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql(client_id: "2", id: 1, title: 'wat', object: {tags: ['ruby', 'java']})

  context 'one->many', ->

    beforeEach ->
      @App = Ember.Namespace.create()
      class @Post extends Ep.Model
        title: Ep.attr('string')
      @App.Post = @Post

      class @Comment extends Ep.Model
        post: Ep.belongsTo(@Post)
      @App.Comment = @Comment

      @Post.reopen
        comments: Ep.hasMany(@Comment)

      @container.register 'model:post', @Post
      @container.register 'model:comment', @Comment

    it 'deserializes null hasMany', ->
      data = {post: [{id: 1, title: 'wat', comment_ids: null}] }
      models = @serializer.deserializePayload(data)
      post = models[0]
      expect(post.comments.length).to.eq(0)

    it 'deserializes null belongsTo', ->
      data = {comments: [{id: 1, title: 'wat', post_id: null}] }
      models = @serializer.deserializePayload(data)
      comment = models[0]
      expect(comment.post).to.be.null

  context 'one->many embedded', ->

    beforeEach ->
      @App = Ember.Namespace.create()
      class @Post extends Ep.Model
        title: Ep.attr('string')
      @App.Post = @Post

      class @Comment extends Ep.Model
        post: Ep.belongsTo(@Post)
      @App.Comment = @Comment

      @Post.reopen
        comments: Ep.hasMany(@Comment)

      PostSerializer = Ep.RestSerializer.extend
        properties:
          comments: { embedded: 'always' }

      @container.register 'serializer:post', PostSerializer

      @container.register 'model:post', @Post
      @container.register 'model:comment', @Comment

    it 'deserializes null belongsTo', ->
      data = {comments: [{id: 1, title: 'wat', post: null}] }
      models = @serializer.deserializePayload(data)
      comment = models[0]
      expect(comment.post).to.be.null

  context 'one->one embedded', ->

    beforeEach ->
      @App = Ember.Namespace.create()
      class @Post extends Ep.Model
        title: Ep.attr('string')
      @App.Post = @Post

      class @User extends Ep.Model
        name: Ep.attr('string')
        post: Ep.belongsTo(@Post)
      @App.User = @User

      @Post.reopen
        user: Ep.belongsTo(@User)

      PostSerializer = Ep.RestSerializer.extend
        properties:
          user: { embedded: 'always' }

      @container.register 'serializer:post', PostSerializer

      @container.register 'model:post', @Post
      @container.register 'model:user', @User

    it 'deserializes null belongsTo', ->
      data = {posts: [{id: 1, title: 'wat', user: null}] }
      models = @serializer.deserializePayload(data)
      post = models[0]
      expect(post.user).to.be.null

  describe 'meta data', ->
    it 'adds meta data to existing object', ->
      obj = {title: 'wat'}
      data = {meta: {recursive: 'meta'}}
      obj = @serializer.setMeta(data, obj)
      expect(obj.meta).to.exist
      expect(obj.meta.recursive).to.exist
      expect(obj.meta.recursive).to.eq('meta')

    it 'adds creates an object with meta if object undefined', ->
      data = {meta: {recursive: 'meta'}}
      obj = @serializer.setMeta(data, obj)
      expect(obj.meta).to.exist
      expect(obj.meta.recursive).to.exist
      expect(obj.meta.recursive).to.eq('meta')

describe 'Ep.RestSerializer', ->

  beforeEach ->
    @Serializer = Ep.RestSerializer.extend()
    @container = new Ember.Container()
    @container.register('serializer:main', @Serializer)

    @container.register('transform:boolean', Ep.BooleanTransform)
    @container.register('transform:date', Ep.DateTransform)
    @container.register('transform:number', Ep.NumberTransform)
    @container.register('transform:string', Ep.StringTransform)

    @serializer = @container.lookup('serializer:main')

  context 'simple model', ->
    beforeEach ->
      @Post = Ep.Model.extend
        title: Ep.attr('string')
        longTitle: Ep.attr('string')
      @container.register 'model:post', @Post

    describe 'deserializePayload', ->

      it 'reads plural hash key', ->
        data = {posts: {id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'reads singular hash key', ->
        data = {post: {id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'reads array value', ->
        data = {post: [{id: 1, title: 'wat', long_title: 'wat omgawd'}] }
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'obeys custom keys', ->
        @serializer.reopen
          properties:
            title:
              key: 'POST_TITLE'
        data = {post: {id: 1, POST_TITLE: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

      it 'reads null client_id as null', ->
        data = {posts: {client_id: null, id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post.clientId).to.be.null

      it 'reads revs', ->
        data = {posts: {rev: 123, client_rev: 321, client_id: 1, id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post.rev).to.eq(123)
        expect(post.clientRev).to.eq(321)

      it 'respects aliases', ->
        @serializer.reopen
          aliases:
            blog_post: 'post'

        data = {blog_post: {id: 1, title: 'wat', long_title: 'wat omgawd'}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.longTitle).to.eq('wat omgawd')
        expect(post.id).to.eq("1")

    describe 'serialization', ->

      it 'serializes', ->
        post = @Post.create
          id: 1
          clientId: "2"
          title: "wat"
          longTitle: 'wat omgawd'
          rev: 123
          clientRev: 321
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql(client_id: "2", id: 1, title: 'wat', long_title: 'wat omgawd', rev: 123, client_rev: 321)

      it 'handles unloaded lazy model', ->
        lazyPost = Ep.LazyModel.create
          id: 1,
          clientId: "2"
          type: @Post
        data = @serializer.serialize(lazyPost, includeId: true)
        expect(data).to.eql client_id: "2", id: 1

      it 'handles loaded lazy model', ->
        post = @Post.create()
        post.id = 1
        post.clientId = "2"
        post.title = 'wat'
        post.longTitle = 'wat omgawd'
        lazyPost = Ep.LazyModel.create
          id: 1,
          type: @Post
        lazyPost.resolve(post)
        data = @serializer.serialize(lazyPost, includeId: true)
        expect(data).to.eql client_id: "2", id: 1, title: 'wat', long_title: 'wat omgawd'

      it 'obeys custom keys', ->
        @serializer.reopen
          properties:
            title:
              key: 'POST_TITLE'
        post = @Post.create()
        post.id = 1
        post.clientId = "2"
        post.title = 'wat'
        post.longTitle = 'wat omgawd'
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql({client_id: "2", id: 1, POST_TITLE: 'wat', long_title: 'wat omgawd'})

  context 'model with raw and object properties', ->

    beforeEach ->
      @Post = Ep.Model.extend
        title: Ep.attr()
        object: Ep.attr()
      @container.register 'model:post', @Post

    describe 'deserializePayload', ->

      it 'deserializes', ->
        data = {posts: {id: 1, title: 'wat', object: {prop: 'sup'}}}
        models = @serializer.deserializePayload(data)
        post = models[0]
        expect(post).to.be.an.instanceof(@Post)
        expect(post.title).to.eq('wat')
        expect(post.object.prop).to.eq('sup')
        expect(post.id).to.eq("1")

    describe 'serialization', ->

      it 'serializes', ->
        post = @Post.create
          id: 1
          clientId: "2"
          title: "wat"
          object: {prop: 'sup'}
        data = @serializer.serialize(post, includeId: true)
        expect(data).to.eql(client_id: "2", id: 1, title: 'wat', object: {prop: 'sup'})

  context 'one->many', ->

    beforeEach ->
      @App = Ember.Namespace.create()
      class @Post extends Ep.Model
        title: Ep.attr('string')
      @App.Post = @Post

      class @Comment extends Ep.Model
        post: Ep.belongsTo(@Post)
      @App.Comment = @Comment

      @Post.reopen
        comments: Ep.hasMany(@Comment)

      @container.register 'model:post', @Post
      @container.register 'model:comment', @Comment

    it 'deserializes null hasMany', ->
      data = {post: [{id: 1, title: 'wat', comment_ids: null}] }
      models = @serializer.deserializePayload(data)
      post = models[0]
      expect(post.comments.length).to.eq(0)

    it 'deserializes null belongsTo', ->
      data = {comments: [{id: 1, title: 'wat', post_id: null}] }
      models = @serializer.deserializePayload(data)
      comment = models[0]
      expect(comment.post).to.be.null

  context 'one->many embedded', ->

    beforeEach ->
      @App = Ember.Namespace.create()
      class @Post extends Ep.Model
        title: Ep.attr('string')
      @App.Post = @Post

      class @Comment extends Ep.Model
        post: Ep.belongsTo(@Post)
      @App.Comment = @Comment

      @Post.reopen
        comments: Ep.hasMany(@Comment)

      PostSerializer = Ep.RestSerializer.extend
        properties:
          comments: { embedded: 'always' }

      @container.register 'serializer:post', PostSerializer

      @container.register 'model:post', @Post
      @container.register 'model:comment', @Comment

    it 'deserializes null belongsTo', ->
      data = {comments: [{id: 1, title: 'wat', post: null}] }
      models = @serializer.deserializePayload(data)
      comment = models[0]
      expect(comment.post).to.be.null

  context 'one->one embedded', ->

    beforeEach ->
      @App = Ember.Namespace.create()
      class @Post extends Ep.Model
        title: Ep.attr('string')
      @App.Post = @Post

      class @User extends Ep.Model
        name: Ep.attr('string')
        post: Ep.belongsTo(@Post)
      @App.User = @User

      @Post.reopen
        user: Ep.belongsTo(@User)

      PostSerializer = Ep.RestSerializer.extend
        properties:
          user: { embedded: 'always' }

      @container.register 'serializer:post', PostSerializer

      @container.register 'model:post', @Post
      @container.register 'model:user', @User

    it 'deserializes null belongsTo', ->
      data = {posts: [{id: 1, title: 'wat', user: null}] }
      models = @serializer.deserializePayload(data)
      post = models[0]
      expect(post.user).to.be.null

  describe 'meta data', ->
    it 'adds meta data to existing object', ->
      obj = {title: 'wat'}
      data = {meta: {recursive: 'meta'}}
      obj = @serializer.setMeta(data, obj)
      expect(obj.meta).to.exist
      expect(obj.meta.recursive).to.exist
      expect(obj.meta.recursive).to.eq('meta')

    it 'adds creates an object with meta if object undefined', ->
      data = {meta: {recursive: 'meta'}}
      obj = @serializer.setMeta(data, obj)
      expect(obj.meta).to.exist
      expect(obj.meta.recursive).to.exist
      expect(obj.meta.recursive).to.eq('meta')