Commit eeb55dc2 authored by Thomas Ives's avatar Thomas Ives
Browse files

Merge branch 'fix-crash-on-do-nothing-read-attribute' into 'main'

Attribute::reset_value: Clear the data_size to 0

In some places, we use the data_size to know if we are allowed to access
the storage.

Closes #1585.

See merge request tango-controls/cppTango!1558
parents eac507fb 634d9119
Loading
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1580,6 +1580,7 @@ class Attribute
    void reset_value()
    {
        attribute_value.reset();
        data_size = 0;
    }

    bool value_is_set() const
+1 −2
Original line number Diff line number Diff line
@@ -2414,8 +2414,7 @@ void Attribute::delete_seq_and_reset_alarm()
void Attribute::delete_seq()
{
    TANGO_LOG_DEBUG << "Attribute::delete_seq() called " << std::endl;
    attribute_value.reset();
    data_size = 0;
    reset_value();
}

//+-------------------------------------------------------------------------
+1 −1
Original line number Diff line number Diff line
@@ -1010,7 +1010,7 @@ void Device_3Impl::read_attributes_no_except(const Tango::DevVarStringArray &nam
                    // Check alarm
                    //

                    if((att.is_alarmed().any()) && (att.get_quality() != Tango::ATTR_INVALID))
                    if(att.value_is_set() && (att.is_alarmed().any()) && (att.get_quality() != Tango::ATTR_INVALID))
                    {
                        att.check_alarm();
                    }
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ tango_catch2_tests_create(
    catch2_attr_polling.cpp
    catch2_attr_polling_history.cpp
    catch2_attr_read_write_simple.cpp
    catch2_attr_read_no_set.cpp
    catch2_cmd_polling.cpp
    catch2_cmd_polling_history.cpp
    catch2_cmd_query.cpp
+97 −0
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2025 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
#include "catch2_common.h"

template <class Base>
class AttrReadNoSet : public Base
{
  public:
    using Base::Base;

    void init_device() override { }

    void read_attr(Tango::Attribute &) override
    {
        /* Do nothing */
    }

    void set_value()
    {
        auto &att = Base::get_device_attr()->get_attr_by_name("attr_read_no_set");
        value = 1.0;
        att.set_value(&value);
    }

    static void attribute_factory(std::vector<Tango::Attr *> &attrs)
    {
        attrs.push_back(new TangoTest::AutoAttr<&AttrReadNoSet::read_attr>("attr_read_no_set", Tango::DEV_DOUBLE));
        Tango::UserDefaultAttrProp props;
        props.set_max_alarm("10.0");
        attrs.back()->set_default_properties(props);
    }

    static void command_factory(std::vector<Tango::Command *> &cmds)
    {
        cmds.push_back(new TangoTest::AutoCommand<&AttrReadNoSet::set_value>("SetValue"));
    }

  private:
    Tango::DevDouble value;
};

TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(AttrReadNoSet, 3)

SCENARIO("Not setting a value")
{
    int idlver = GENERATE(TangoTest::idlversion(3));
    GIVEN("a device proxy to a simple IDLv" << idlver << " device")
    {
        TangoTest::Context ctx{"attr_read_no_set", "AttrReadNoSet", idlver};
        auto device = ctx.get_proxy();

        REQUIRE(idlver == device->get_idl_version());

        WHEN("we read a do nothing attribute")
        {
            std::string att{"attr_read_no_set"};

            Tango::DeviceAttribute da;
            REQUIRE_NOTHROW(da = device->read_attribute(att));

            THEN("we get a API_AttrValueNotSet exception")
            {
                using namespace TangoTest::Matchers;
                using namespace Catch::Matchers;

                double val_read{};
                REQUIRE_THROWS_MATCHES(
                    da >> val_read, Tango::DevFailed, ErrorListMatches(AnyMatch(Reason(Tango::API_AttrValueNotSet))));
            }
        }

        WHEN("we set an attribute value out-of-band")
        {
            REQUIRE_NOTHROW(device->command_inout("SetValue"));

            AND_WHEN("we read the do nothing attribute")
            {
                std::string att{"attr_read_no_set"};

                Tango::DeviceAttribute da;
                REQUIRE_NOTHROW(da = device->read_attribute(att));

                THEN("we get a API_AttrValueNotSet exception")
                {
                    using namespace TangoTest::Matchers;

                    double val_read{};
                    REQUIRE_THROWS_MATCHES(
                        da >> val_read, Tango::DevFailed, FirstErrorMatches(Reason(Tango::API_AttrValueNotSet)));
                }
            }
        }
    }
}