<commit_before>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2016 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <vector>

#include "joynr/MessagingQos.h"
#include "joynr/JoynrMessage.h"
#include "joynr/JoynrMessageFactory.h"
#include "joynr/JoynrMessageSender.h"
#include "joynr/Request.h"
#include "joynr/Reply.h"
#include "joynr/SubscriptionPublication.h"
#include "joynr/PeriodicSubscriptionQos.h"
#include "joynr/OnChangeSubscriptionQos.h"
#include "tests/utils/MockObjects.h"
#include "joynr/SingleThreadedIOService.h"

using ::testing::A;
using ::testing::_;
using ::testing::A;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::AllOf;
using ::testing::Property;
using namespace joynr;

class JoynrMessageSenderTest : public ::testing::Test {
public:
    JoynrMessageSenderTest() :
        messageFactory(),
        postFix(),
        senderID(),
        receiverID(),
        requestID(),
        qosSettings(),
        mockDispatcher(),
        mockMessagingStub(),
        callBack(),
        singleThreadedIOService()
    {
        singleThreadedIOService.start();
    }

    void SetUp(){
        postFix = "_" + util::createUuid();
        senderID = "senderId" + postFix;
        receiverID = "receiverID" + postFix;
        requestID = "requestId" + postFix;
        qosSettings = MessagingQos(456000);
    }

protected:
    JoynrMessageFactory messageFactory;
    std::string postFix;
    std::string senderID;
    std::string receiverID;
    std::string requestID;
    MessagingQos qosSettings;
    MockDispatcher mockDispatcher;
    MockMessaging mockMessagingStub;
    std::shared_ptr<IReplyCaller> callBack;
    SingleThreadedIOService singleThreadedIOService;
};

typedef JoynrMessageSenderTest JoynrMessageSenderDeathTest;

TEST_F(JoynrMessageSenderTest, sendRequest_normal){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());

    Request request;
    request.setMethodName("methodName");
    request.setParams(42, std::string("value"));
    std::vector<std::string> paramDatatypes;
    paramDatatypes.push_back("java.lang.Integer");
    paramDatatypes.push_back("java.lang.String");
    request.setParamDatatypes(paramDatatypes);

    JoynrMessage message = messageFactory.createRequest(
                senderID,
                receiverID,
                qosSettings,
                request
    );

    EXPECT_CALL( *(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_REQUEST)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    joynrMessageSender.sendRequest(senderID, receiverID, qosSettings, request, callBack);
}

TEST_F(JoynrMessageSenderTest, sendOneWayRequest_normal){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());

    OneWayRequest oneWayRequest;
    oneWayRequest.setMethodName("methodName");
    oneWayRequest.setParams(42, std::string("value"));
    std::vector<std::string> paramDatatypes;
    paramDatatypes.push_back("java.lang.Integer");
    paramDatatypes.push_back("java.lang.String");
    oneWayRequest.setParamDatatypes(paramDatatypes);

    JoynrMessage message = messageFactory.createOneWayRequest(
                senderID,
                receiverID,
                qosSettings,
                oneWayRequest
    );

    EXPECT_CALL( *(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_ONE_WAY)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    joynrMessageSender.sendOneWayRequest(senderID, receiverID, qosSettings, oneWayRequest);
}

TEST_F(JoynrMessageSenderDeathTest, DISABLED_sendRequest_nullPayloadFails_death){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());
    EXPECT_CALL(*(messagingStub.get()), route(_,_)).Times(0);

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

    Request jsonRequest;
    ASSERT_DEATH(joynrMessageSender.sendRequest(senderID, receiverID, qosSettings, jsonRequest, callBack), "Assertion.*");
}

TEST_F(JoynrMessageSenderTest, sendReply_normal){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    Reply reply;
    reply.setRequestReplyId(util::createUuid());
    reply.setResponse(std::string("response"));

    JoynrMessage message = messageFactory.createReply(
                senderID,
                receiverID,
                qosSettings,
                reply);

    EXPECT_CALL(*(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_REPLY)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    joynrMessageSender.sendReply(senderID, receiverID, qosSettings, reply);
}

TEST_F(JoynrMessageSenderTest, sendSubscriptionRequest_normal){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());

    std::int64_t period = 2000;
    std::int64_t validity = 100000;
    std::int64_t alert = 4000;
    auto qos = std::make_shared<PeriodicSubscriptionQos>(validity, period, alert);

    SubscriptionRequest subscriptionRequest;
    subscriptionRequest.setSubscriptionId("subscriptionId");
    subscriptionRequest.setSubscribeToName("attributeName");
    subscriptionRequest.setQos(qos);

    JoynrMessage message = messageFactory.createSubscriptionRequest(
                senderID,
                receiverID,
                qosSettings,
                subscriptionRequest);

    EXPECT_CALL(*messagingStub, route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_SUBSCRIPTION_REQUEST)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

    joynrMessageSender.sendSubscriptionRequest(senderID, receiverID, qosSettings, subscriptionRequest);
}

TEST_F(JoynrMessageSenderTest, sendBroadcastSubscriptionRequest_normal){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());

    std::int64_t minInterval = 2000;
    std::int64_t validity = 100000;
    auto qos = std::make_shared<OnChangeSubscriptionQos>(validity, minInterval);

    BroadcastSubscriptionRequest subscriptionRequest;
    BroadcastFilterParameters filter;
    filter.setFilterParameter("MyParameter", "MyValue");
    subscriptionRequest.setFilterParameters(filter);
    subscriptionRequest.setSubscriptionId("subscriptionId");
    subscriptionRequest.setSubscribeToName("broadcastName");
    subscriptionRequest.setQos(qos);

    JoynrMessage message = messageFactory.createBroadcastSubscriptionRequest(
                senderID,
                receiverID,
                qosSettings,
                subscriptionRequest);

    EXPECT_CALL(*messagingStub, route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_BROADCAST_SUBSCRIPTION_REQUEST)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

    joynrMessageSender.sendBroadcastSubscriptionRequest(senderID, receiverID, qosSettings, subscriptionRequest);
}

//TODO implement sending a reply to a subscription request!
TEST_F(JoynrMessageSenderTest, DISABLED_sendSubscriptionReply_normal){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());
    std::string payload("subscriptionReply");
    EXPECT_CALL(*(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_SUBSCRIPTION_REPLY)),
                                                  Property(&JoynrMessage::getPayload, Eq(payload))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

//    joynrMessageSender.sendSubscriptionReply(util::createUuid(), payload, senderID, receiverID, qosSettings);
}

TEST_F(JoynrMessageSenderTest, sendPublication_normal){

    MockDispatcher mockDispatcher;
    auto messagingStub = std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService());

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    SubscriptionPublication publication;
    publication.setSubscriptionId("ignoresubscriptionid");
    publication.setResponse(std::string("publication"));
    JoynrMessage message = messageFactory.createSubscriptionPublication(
                senderID,
                receiverID,
                qosSettings,
                publication);

    EXPECT_CALL(*(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_PUBLICATION)),
                                                      Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    joynrMessageSender.sendSubscriptionPublication(senderID, receiverID, qosSettings, std::move(publication));
}
<commit_msg>[C++] JoynrMessageSenderTest: Move common code into fixture<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2016 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <vector>

#include "joynr/MessagingQos.h"
#include "joynr/JoynrMessage.h"
#include "joynr/JoynrMessageFactory.h"
#include "joynr/JoynrMessageSender.h"
#include "joynr/Request.h"
#include "joynr/Reply.h"
#include "joynr/SubscriptionPublication.h"
#include "joynr/PeriodicSubscriptionQos.h"
#include "joynr/OnChangeSubscriptionQos.h"
#include "tests/utils/MockObjects.h"
#include "joynr/SingleThreadedIOService.h"

using ::testing::A;
using ::testing::_;
using ::testing::A;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::AllOf;
using ::testing::Property;
using namespace joynr;

class JoynrMessageSenderTest : public ::testing::Test {
public:
    JoynrMessageSenderTest() :
        messageFactory(),
        postFix(),
        senderID(),
        receiverID(),
        requestID(),
        qosSettings(),
        mockDispatcher(),
        mockMessagingStub(),
        callBack(),
        singleThreadedIOService(),
        messagingStub(std::make_shared<MockMessageRouter>(singleThreadedIOService.getIOService()))
    {
        singleThreadedIOService.start();
    }

    void SetUp(){
        postFix = "_" + util::createUuid();
        senderID = "senderId" + postFix;
        receiverID = "receiverID" + postFix;
        requestID = "requestId" + postFix;
        qosSettings = MessagingQos(456000);
    }

protected:
    JoynrMessageFactory messageFactory;
    std::string postFix;
    std::string senderID;
    std::string receiverID;
    std::string requestID;
    MessagingQos qosSettings;
    MockDispatcher mockDispatcher;
    MockMessaging mockMessagingStub;
    std::shared_ptr<IReplyCaller> callBack;
    SingleThreadedIOService singleThreadedIOService;
    std::shared_ptr<MockMessageRouter> messagingStub;
};

typedef JoynrMessageSenderTest JoynrMessageSenderDeathTest;

TEST_F(JoynrMessageSenderTest, sendRequest_normal){
    Request request;
    request.setMethodName("methodName");
    request.setParams(42, std::string("value"));
    std::vector<std::string> paramDatatypes;
    paramDatatypes.push_back("java.lang.Integer");
    paramDatatypes.push_back("java.lang.String");
    request.setParamDatatypes(paramDatatypes);

    JoynrMessage message = messageFactory.createRequest(
                senderID,
                receiverID,
                qosSettings,
                request
    );

    EXPECT_CALL( *(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_REQUEST)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    joynrMessageSender.sendRequest(senderID, receiverID, qosSettings, request, callBack);
}

TEST_F(JoynrMessageSenderTest, sendOneWayRequest_normal){
    OneWayRequest oneWayRequest;
    oneWayRequest.setMethodName("methodName");
    oneWayRequest.setParams(42, std::string("value"));
    std::vector<std::string> paramDatatypes;
    paramDatatypes.push_back("java.lang.Integer");
    paramDatatypes.push_back("java.lang.String");
    oneWayRequest.setParamDatatypes(paramDatatypes);

    JoynrMessage message = messageFactory.createOneWayRequest(
                senderID,
                receiverID,
                qosSettings,
                oneWayRequest
    );

    EXPECT_CALL( *(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_ONE_WAY)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    joynrMessageSender.sendOneWayRequest(senderID, receiverID, qosSettings, oneWayRequest);
}

TEST_F(JoynrMessageSenderDeathTest, DISABLED_sendRequest_nullPayloadFails_death){
    EXPECT_CALL(*(messagingStub.get()), route(_,_)).Times(0);

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

    Request jsonRequest;
    ASSERT_DEATH(joynrMessageSender.sendRequest(senderID, receiverID, qosSettings, jsonRequest, callBack), "Assertion.*");
}

TEST_F(JoynrMessageSenderTest, sendReply_normal){
    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    Reply reply;
    reply.setRequestReplyId(util::createUuid());
    reply.setResponse(std::string("response"));

    JoynrMessage message = messageFactory.createReply(
                senderID,
                receiverID,
                qosSettings,
                reply);

    EXPECT_CALL(*(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_REPLY)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    joynrMessageSender.sendReply(senderID, receiverID, qosSettings, reply);
}

TEST_F(JoynrMessageSenderTest, sendSubscriptionRequest_normal){
    std::int64_t period = 2000;
    std::int64_t validity = 100000;
    std::int64_t alert = 4000;
    auto qos = std::make_shared<PeriodicSubscriptionQos>(validity, period, alert);

    SubscriptionRequest subscriptionRequest;
    subscriptionRequest.setSubscriptionId("subscriptionId");
    subscriptionRequest.setSubscribeToName("attributeName");
    subscriptionRequest.setQos(qos);

    JoynrMessage message = messageFactory.createSubscriptionRequest(
                senderID,
                receiverID,
                qosSettings,
                subscriptionRequest);

    EXPECT_CALL(*messagingStub, route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_SUBSCRIPTION_REQUEST)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

    joynrMessageSender.sendSubscriptionRequest(senderID, receiverID, qosSettings, subscriptionRequest);
}

TEST_F(JoynrMessageSenderTest, sendBroadcastSubscriptionRequest_normal){
    std::int64_t minInterval = 2000;
    std::int64_t validity = 100000;
    auto qos = std::make_shared<OnChangeSubscriptionQos>(validity, minInterval);

    BroadcastSubscriptionRequest subscriptionRequest;
    BroadcastFilterParameters filter;
    filter.setFilterParameter("MyParameter", "MyValue");
    subscriptionRequest.setFilterParameters(filter);
    subscriptionRequest.setSubscriptionId("subscriptionId");
    subscriptionRequest.setSubscribeToName("broadcastName");
    subscriptionRequest.setQos(qos);

    JoynrMessage message = messageFactory.createBroadcastSubscriptionRequest(
                senderID,
                receiverID,
                qosSettings,
                subscriptionRequest);

    EXPECT_CALL(*messagingStub, route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_BROADCAST_SUBSCRIPTION_REQUEST)),
                                                  Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

    joynrMessageSender.sendBroadcastSubscriptionRequest(senderID, receiverID, qosSettings, subscriptionRequest);
}

//TODO implement sending a reply to a subscription request!
TEST_F(JoynrMessageSenderTest, DISABLED_sendSubscriptionReply_normal){
    std::string payload("subscriptionReply");
    EXPECT_CALL(*(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_SUBSCRIPTION_REPLY)),
                                                  Property(&JoynrMessage::getPayload, Eq(payload))),_));

    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);

//    joynrMessageSender.sendSubscriptionReply(util::createUuid(), payload, senderID, receiverID, qosSettings);
}

TEST_F(JoynrMessageSenderTest, sendPublication_normal){
    JoynrMessageSender joynrMessageSender(messagingStub);
    joynrMessageSender.registerDispatcher(&mockDispatcher);
    SubscriptionPublication publication;
    publication.setSubscriptionId("ignoresubscriptionid");
    publication.setResponse(std::string("publication"));
    JoynrMessage message = messageFactory.createSubscriptionPublication(
                senderID,
                receiverID,
                qosSettings,
                publication);

    EXPECT_CALL(*(messagingStub.get()), route(AllOf(Property(&JoynrMessage::getType, Eq(JoynrMessage::VALUE_MESSAGE_TYPE_PUBLICATION)),
                                                      Property(&JoynrMessage::getPayload, Eq(message.getPayload()))),_));

    joynrMessageSender.sendSubscriptionPublication(senderID, receiverID, qosSettings, std::move(publication));
}
<|endoftext|>


<commit_before>// RUN: %clang_cc1 -DSETNODEBUG=0 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=YESINFO
// RUN: %clang_cc1 -DSETNODEBUG=1 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=NOINFO

#if SETNODEBUG
#define NODEBUG __attribute__((nodebug))
#else
#define NODEBUG
#endif

// Const global variable. Use it so it gets emitted.
NODEBUG static const int const_global_int_def = 1;
void func1(int);
void func2() { func1(const_global_int_def); }
// YESINFO-DAG: !DIGlobalVariable(name: "const_global_int_def"
// NOINFO-NOT:  !DIGlobalVariable(name: "const_global_int_def"

// Global variable with a more involved type.
// If the variable has no debug info, the type should not appear either.
struct S1 {
  int a;
  int b;
};
NODEBUG S1 global_struct = { 2, 3 };
// YESINFO-DAG: !DICompositeType({{.*}} name: "S1"
// NOINFO-NOT:  !DICompositeType({{.*}} name: "S1"
// YESINFO-DAG: !DIGlobalVariable(name: "global_struct"
// NOINFO-NOT:  !DIGlobalVariable(name: "global_struct"

// Static data members. Const member needs a use.
struct S2 {
  NODEBUG static int static_member;
  NODEBUG static const int static_const_member = 4;
};
int S2::static_member = 5;
void func3() { func1(S2::static_const_member); }
// YESINFO-DAG: !DIGlobalVariable(name: "static_member"
// NOINFO-NOT:  !DIGlobalVariable(name: "static_member"
// YESINFO-DAG: !DIDerivedType({{.*}} name: "static_const_member"
// NOINFO-NOT:  !DIDerivedType({{.*}} name: "static_const_member"

// Function-local static variable.
void func4() {
  NODEBUG static int static_local = 6;
}
// YESINFO-DAG: !DIGlobalVariable(name: "static_local"
// NOINFO-NOT:  !DIGlobalVariable(name: "static_local"
<commit_msg>Make the test exercise all paths modified in r267746.<commit_after>// RUN: %clang_cc1 -DSETNODEBUG=0 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=YESINFO
// RUN: %clang_cc1 -DSETNODEBUG=1 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=NOINFO

#if SETNODEBUG
#define NODEBUG __attribute__((nodebug))
#else
#define NODEBUG
#endif

// Const global variable. Use it so it gets emitted.
NODEBUG static const int const_global_int_def = 1;
void func1(int);
void func2() { func1(const_global_int_def); }
// YESINFO-DAG: !DIGlobalVariable(name: "const_global_int_def"
// NOINFO-NOT:  !DIGlobalVariable(name: "const_global_int_def"

// Global variable with a more involved type.
// If the variable has no debug info, the type should not appear either.
struct S1 {
  int a;
  int b;
};
NODEBUG S1 global_struct = { 2, 3 };
// YESINFO-DAG: !DICompositeType({{.*}} name: "S1"
// NOINFO-NOT:  !DICompositeType({{.*}} name: "S1"
// YESINFO-DAG: !DIGlobalVariable(name: "global_struct"
// NOINFO-NOT:  !DIGlobalVariable(name: "global_struct"

// Static data members. Const member needs a use.
// Also the class as a whole needs a use, so that we produce debug info for
// the entire class (iterating over the members, demonstrably skipping those
// with 'nodebug').
struct S2 {
  NODEBUG static int static_member;
  NODEBUG static const int static_const_member = 4;
};
int S2::static_member = 5;
void func3() {
  S2 junk;
  func1(S2::static_const_member);
}
// YESINFO-DAG: !DIGlobalVariable(name: "static_member"
// NOINFO-NOT:  !DIGlobalVariable(name: "static_member"
// YESINFO-DAG: !DIDerivedType({{.*}} name: "static_const_member"
// NOINFO-NOT:  !DIDerivedType({{.*}} name: "static_const_member"

// Function-local static variable.
void func4() {
  NODEBUG static int static_local = 6;
}
// YESINFO-DAG: !DIGlobalVariable(name: "static_local"
// NOINFO-NOT:  !DIGlobalVariable(name: "static_local"
<|endoftext|>


<commit_before>/*
 * Copyright 2019 The Open GEE Contributors
 *
 * 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 "AssetVersion.h"
#include "gee_version.h"
#include "StateUpdater.h"

#include <algorithm>
#include <assert.h>
#include <gtest/gtest.h>
#include <map>
#include <string>

using namespace std;

// All refs must end with "?version=X" or the calls to AssetVersionRef::Bind
// will try to load assets from disk. For simplicity, we force everything to
// be version 1. When you write additional tests you have to remember to call
// this function when working directly with the state updater.
const string SUFFIX = "?version=1";
AssetKey fix(AssetKey ref) {
  return ref.toString() + SUFFIX;
}

class MockVersion : public AssetVersionImpl {
  public:
    bool stateSet;
    bool loadedMutable;
    vector<AssetKey> dependents;

    MockVersion() : stateSet(false), loadedMutable(false) {
      type = AssetDefs::Imagery;
      state = AssetDefs::Blocked;
    }
    MockVersion(const AssetKey & ref) : MockVersion() {
      name = fix(ref);
    }
    MockVersion(const MockVersion & that) : MockVersion() {
      name = that.name; // Don't add the suffix - the other MockVersion already did
    }
    void DependentChildren(vector<SharedString> & d) const {
      for(auto dependent : dependents) {
        d.push_back(dependent);
      }
    }
    bool NeedComputeState() const { return true; }
    AssetDefs::State CalcStateByInputsAndChildren(
        AssetDefs::State stateByInputs,
        AssetDefs::State stateByChildren,
        bool blockersAreOffline,
        uint32 numWaitingFor) const
    {
      return AssetDefs::InProgress;
    }
    void SetMyStateOnly(AssetDefs::State newState, bool sendNotifications) {
      stateSet = true;
    }

    // Not used - only included to make MockVersion non-virtual
    string PluginName(void) const { return string(); }
    void GetOutputFilenames(vector<string> &) const {}
    string GetOutputFilename(uint) const { return string(); }
};

using VersionMap = map<AssetKey, shared_ptr<MockVersion>>;

class MockStorageManager : public StorageManagerInterface<AssetVersionImpl> {
  private:
    VersionMap versions;
    PointerType GetFromMap(const AssetKey & ref) {
      auto versionIter = versions.find(ref);
      if (versionIter != versions.end()) {
        auto version = dynamic_pointer_cast<AssetVersionImpl>(versionIter->second);
        assert(version);
        return version;
      }
      assert(false);
      return nullptr;
    }
  public:
    void AddVersion(const AssetKey & key, const MockVersion & v) {
      versions[key] = make_shared<MockVersion>(v);
    }
    virtual AssetHandle<const AssetVersionImpl> Get(const AssetKey &ref) {
      return AssetHandle<const AssetVersionImpl>(GetFromMap(ref), nullptr);
    }
    virtual AssetHandle<AssetVersionImpl> GetMutable(const AssetKey &ref) {
      auto handle = AssetHandle<AssetVersionImpl>(GetFromMap(ref), nullptr);
      dynamic_cast<MockVersion*>(handle.operator->())->loadedMutable = true;
      return handle;
    }
    void ResetLoadedMutable() {
      for (auto & v : versions) {
        v.second->loadedMutable = false;
      }
    }
};

class StateUpdaterTest : public testing::Test {
  protected:
   MockStorageManager sm;
   StateUpdater updater;
  public:
   StateUpdaterTest() : sm(), updater(&sm) {}
};

void SetVersions(MockStorageManager & sm, vector<MockVersion> versions) {
  for (auto & version: versions) {
    sm.AddVersion(version.name, version);
  }
}

// This is bad because technically the object could be deleted before you use
// it, but it works for unit tests.
AssetHandle<const MockVersion> GetVersion(MockStorageManager & sm, const AssetKey & key) {
  return sm.Get(fix(key));
}

void SetParentChild(MockStorageManager & sm, AssetKey parent, AssetKey child) {
  parent = fix(parent);
  child = fix(child);
  sm.GetMutable(parent)->children.push_back(child);
  sm.GetMutable(child)->parents.push_back(parent);
}

void SetListenerInput(MockStorageManager & sm, AssetKey listener, AssetKey input) {
  listener = fix(listener);
  input = fix(input);
  sm.GetMutable(listener)->inputs.push_back(input);
  sm.GetMutable(input)->listeners.push_back(listener);
}

void SetDependent(MockStorageManager & sm, AssetKey dependee, AssetKey dependent) {
  dependee = fix(dependee);
  dependent = fix(dependent);
  AssetHandle<MockVersion> dependeeHandle = sm.GetMutable(dependee);
  dependeeHandle->dependents.push_back(dependent);
}

void GetBigTree(MockStorageManager & sm) {
  SetVersions(sm,
              {
                MockVersion("gp"),
                MockVersion("p1"),
                MockVersion("p2"),
                MockVersion("gpi"),
                MockVersion("pi1"),
                MockVersion("c1"),
                MockVersion("c2"),
                MockVersion("c3"),
                MockVersion("c4"),
                MockVersion("ci1"),
                MockVersion("ci2"),
                MockVersion("ci3")
              });
  SetParentChild(sm, "gp", "p1");
  SetParentChild(sm, "gp", "p2");
  SetListenerInput(sm, "gp", "gpi");
  SetListenerInput(sm, "p1", "pi1");
  SetParentChild(sm, "p1", "c1");
  SetParentChild(sm, "p1", "c2");
  SetParentChild(sm, "p2", "c3");
  SetParentChild(sm, "p2", "c4");
  SetListenerInput(sm, "c2", "ci3");
  SetListenerInput(sm, "c4", "ci1");
  SetListenerInput(sm, "c4", "ci2");
  SetDependent(sm, "gp", "p1");
  SetDependent(sm, "gp", "c2");
  SetDependent(sm, "p1", "c1");
}

TEST_F(StateUpdaterTest, SetStateSingleVersion) {
  AssetKey ref1 = "test1";
  AssetKey ref2 = "test2";
  AssetKey ref3 = "test3";
  SetVersions(sm, {MockVersion(ref1), MockVersion(ref2), MockVersion(ref3)});
  updater.SetStateForRefAndDependents(fix(ref1), AssetDefs::Bad, [](AssetDefs::State) { return true; });
  ASSERT_TRUE(GetVersion(sm, ref1)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
  updater.SetStateForRefAndDependents(fix(ref2), AssetDefs::Bad, [](AssetDefs::State) { return false; });
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersions) {
  GetBigTree(sm);
  updater.SetStateForRefAndDependents(fix("gp"), AssetDefs::Canceled, [](AssetDefs::State) {return true; });

  ASSERT_TRUE(GetVersion(sm, "gp")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c2")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersionsFromChild) {
  GetBigTree(sm);
  sm.ResetLoadedMutable();
  updater.SetStateForRefAndDependents(fix("p1"), AssetDefs::Canceled, [](AssetDefs::State) {return true; });

  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gp")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);

  ASSERT_TRUE(GetVersion(sm, "p1")->loadedMutable);
  ASSERT_TRUE(GetVersion(sm, "c1")->loadedMutable);

  ASSERT_FALSE(GetVersion(sm, "gp")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "gpi")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "pi1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c3")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c4")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci3")->loadedMutable);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc,argv);
  return RUN_ALL_TESTS();
}
<commit_msg>Test states that do and don't change<commit_after>/*
 * Copyright 2019 The Open GEE Contributors
 *
 * 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 "AssetVersion.h"
#include "gee_version.h"
#include "StateUpdater.h"

#include <algorithm>
#include <assert.h>
#include <gtest/gtest.h>
#include <map>
#include <string>

using namespace std;

// All refs must end with "?version=X" or the calls to AssetVersionRef::Bind
// will try to load assets from disk. For simplicity, we force everything to
// be version 1. When you write additional tests you have to remember to call
// this function when working directly with the state updater.
const string SUFFIX = "?version=1";
AssetKey fix(AssetKey ref) {
  return ref.toString() + SUFFIX;
}

class MockVersion : public AssetVersionImpl {
  public:
    bool stateSet;
    bool loadedMutable;
    vector<AssetKey> dependents;

    MockVersion() : stateSet(false), loadedMutable(false) {
      type = AssetDefs::Imagery;
      state = AssetDefs::Blocked;
    }
    MockVersion(const AssetKey & ref) : MockVersion() {
      name = fix(ref);
    }
    MockVersion(const MockVersion & that) : MockVersion() {
      name = that.name; // Don't add the suffix - the other MockVersion already did
    }
    void DependentChildren(vector<SharedString> & d) const {
      for(auto dependent : dependents) {
        d.push_back(dependent);
      }
    }
    bool NeedComputeState() const { return true; }
    AssetDefs::State CalcStateByInputsAndChildren(
        AssetDefs::State stateByInputs,
        AssetDefs::State stateByChildren,
        bool blockersAreOffline,
        uint32 numWaitingFor) const
    {
      return AssetDefs::InProgress;
    }
    void SetMyStateOnly(AssetDefs::State newState, bool sendNotifications) {
      stateSet = true;
    }

    // Not used - only included to make MockVersion non-virtual
    string PluginName(void) const { return string(); }
    void GetOutputFilenames(vector<string> &) const {}
    string GetOutputFilename(uint) const { return string(); }
};

using VersionMap = map<AssetKey, shared_ptr<MockVersion>>;

class MockStorageManager : public StorageManagerInterface<AssetVersionImpl> {
  private:
    VersionMap versions;
    PointerType GetFromMap(const AssetKey & ref) {
      auto versionIter = versions.find(ref);
      if (versionIter != versions.end()) {
        auto version = dynamic_pointer_cast<AssetVersionImpl>(versionIter->second);
        assert(version);
        return version;
      }
      assert(false);
      return nullptr;
    }
  public:
    void AddVersion(const AssetKey & key, const MockVersion & v) {
      versions[key] = make_shared<MockVersion>(v);
    }
    virtual AssetHandle<const AssetVersionImpl> Get(const AssetKey &ref) {
      return AssetHandle<const AssetVersionImpl>(GetFromMap(ref), nullptr);
    }
    virtual AssetHandle<AssetVersionImpl> GetMutable(const AssetKey &ref) {
      auto handle = AssetHandle<AssetVersionImpl>(GetFromMap(ref), nullptr);
      dynamic_cast<MockVersion*>(handle.operator->())->loadedMutable = true;
      return handle;
    }
    void ResetLoadedMutable() {
      for (auto & v : versions) {
        v.second->loadedMutable = false;
      }
    }
};

class StateUpdaterTest : public testing::Test {
  protected:
   MockStorageManager sm;
   StateUpdater updater;
  public:
   StateUpdaterTest() : sm(), updater(&sm) {}
};

void SetVersions(MockStorageManager & sm, vector<MockVersion> versions) {
  for (auto & version: versions) {
    sm.AddVersion(version.name, version);
  }
}

// This is bad because technically the object could be deleted before you use
// it, but it works for unit tests.
AssetHandle<const MockVersion> GetVersion(MockStorageManager & sm, const AssetKey & key) {
  return sm.Get(fix(key));
}

void SetParentChild(MockStorageManager & sm, AssetKey parent, AssetKey child) {
  parent = fix(parent);
  child = fix(child);
  sm.GetMutable(parent)->children.push_back(child);
  sm.GetMutable(child)->parents.push_back(parent);
}

void SetListenerInput(MockStorageManager & sm, AssetKey listener, AssetKey input) {
  listener = fix(listener);
  input = fix(input);
  sm.GetMutable(listener)->inputs.push_back(input);
  sm.GetMutable(input)->listeners.push_back(listener);
}

void SetDependent(MockStorageManager & sm, AssetKey dependee, AssetKey dependent) {
  dependee = fix(dependee);
  dependent = fix(dependent);
  AssetHandle<MockVersion> dependeeHandle = sm.GetMutable(dependee);
  dependeeHandle->dependents.push_back(dependent);
}

void GetBigTree(MockStorageManager & sm) {
  SetVersions(sm,
              {
                MockVersion("gp"),
                MockVersion("p1"),
                MockVersion("p2"),
                MockVersion("gpi"),
                MockVersion("pi1"),
                MockVersion("c1"),
                MockVersion("c2"),
                MockVersion("c3"),
                MockVersion("c4"),
                MockVersion("ci1"),
                MockVersion("ci2"),
                MockVersion("ci3")
              });
  SetParentChild(sm, "gp", "p1");
  SetParentChild(sm, "gp", "p2");
  SetListenerInput(sm, "gp", "gpi");
  SetListenerInput(sm, "p1", "pi1");
  SetParentChild(sm, "p1", "c1");
  SetParentChild(sm, "p1", "c2");
  SetParentChild(sm, "p2", "c3");
  SetParentChild(sm, "p2", "c4");
  SetListenerInput(sm, "c2", "ci3");
  SetListenerInput(sm, "c4", "ci1");
  SetListenerInput(sm, "c4", "ci2");
  SetDependent(sm, "gp", "p1");
  SetDependent(sm, "gp", "c2");
  SetDependent(sm, "p1", "c1");
}

TEST_F(StateUpdaterTest, SetStateSingleVersion) {
  AssetKey ref1 = "test1";
  AssetKey ref2 = "test2";
  AssetKey ref3 = "test3";
  SetVersions(sm, {MockVersion(ref1), MockVersion(ref2), MockVersion(ref3)});
  updater.SetStateForRefAndDependents(fix(ref1), AssetDefs::Bad, [](AssetDefs::State) { return true; });
  ASSERT_TRUE(GetVersion(sm, ref1)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
  updater.SetStateForRefAndDependents(fix(ref2), AssetDefs::Bad, [](AssetDefs::State) { return false; });
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersions) {
  GetBigTree(sm);
  updater.SetStateForRefAndDependents(fix("gp"), AssetDefs::Canceled, [](AssetDefs::State) {return true; });

  ASSERT_TRUE(GetVersion(sm, "gp")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c2")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersionsFromChild) {
  GetBigTree(sm);
  sm.ResetLoadedMutable();
  updater.SetStateForRefAndDependents(fix("p1"), AssetDefs::Canceled, [](AssetDefs::State) { return true; });

  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gp")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);

  ASSERT_TRUE(GetVersion(sm, "p1")->loadedMutable);
  ASSERT_TRUE(GetVersion(sm, "c1")->loadedMutable);

  ASSERT_FALSE(GetVersion(sm, "gp")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "gpi")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "pi1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c3")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c4")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci3")->loadedMutable);
}

TEST_F(StateUpdaterTest, SetState_StateDoesAndDoesntChange) {
  const AssetDefs::State SET_TO_STATE = AssetDefs::InProgress;
  const AssetDefs::State STARTING_STATE = AssetDefs::Canceled;
  SetVersions(sm, {MockVersion("a"), MockVersion("b")});
  sm.GetMutable(fix("a"))->state = SET_TO_STATE;
  sm.GetMutable(fix("b"))->state = STARTING_STATE;

  updater.SetStateForRefAndDependents(fix("a"), SET_TO_STATE, [](AssetDefs::State) { return true; });
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "b")->stateSet);

  updater.SetStateForRefAndDependents(fix("b"), SET_TO_STATE, [](AssetDefs::State) { return true; });
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "b")->stateSet);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc,argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2019 The Open GEE Contributors
 *
 * 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 "AssetVersion.h"
#include "gee_version.h"
#include "StateUpdater.h"

#include <algorithm>
#include <assert.h>
#include <gtest/gtest.h>
#include <map>
#include <string>

using namespace std;

// All refs must end with "?version=X" or the calls to AssetVersionRef::Bind
// will try to load assets from disk. For simplicity, we force everything to
// be version 1. When you write additional tests you have to remember to call
// this function when working directly with the state updater.
const string SUFFIX = "?version=1";
AssetKey fix(AssetKey ref) {
  return ref.toString() + SUFFIX;
}

const AssetDefs::State STARTING_STATE = AssetDefs::Blocked;
const AssetDefs::State CALCULATED_STATE = AssetDefs::InProgress;

class MockVersion : public AssetVersionImpl {
  public:
    bool stateSet;
    bool loadedMutable;
    bool notificationsSent;
    bool needComputeState;
    vector<AssetKey> dependents;

    MockVersion() : stateSet(false), loadedMutable(false), notificationsSent(false), needComputeState(true) {
      type = AssetDefs::Imagery;
      state = STARTING_STATE;
    }
    MockVersion(const AssetKey & ref)
        : MockVersion() {
      name = fix(ref);
    }
    MockVersion(const MockVersion & that) : MockVersion() {
      name = that.name; // Don't add the suffix - the other MockVersion already did
    }
    void DependentChildren(vector<SharedString> & d) const {
      for(auto dependent : dependents) {
        d.push_back(dependent);
      }
    }
    bool NeedComputeState() const { return needComputeState; }
    AssetDefs::State CalcStateByInputsAndChildren(
        AssetDefs::State stateByInputs,
        AssetDefs::State stateByChildren,
        bool blockersAreOffline,
        uint32 numWaitingFor) const {
      return CALCULATED_STATE;
    }
    void SetMyStateOnly(AssetDefs::State newState, bool sendNotifications) {
      stateSet = true;
      notificationsSent = sendNotifications;
    }

    // Not used - only included to make MockVersion non-virtual
    string PluginName(void) const { return string(); }
    void GetOutputFilenames(vector<string> &) const {}
    string GetOutputFilename(uint) const { return string(); }
};

using VersionMap = map<AssetKey, shared_ptr<MockVersion>>;

class MockStorageManager : public StorageManagerInterface<AssetVersionImpl> {
  private:
    VersionMap versions;
    PointerType GetFromMap(const AssetKey & ref) {
      auto versionIter = versions.find(ref);
      if (versionIter != versions.end()) {
        auto version = dynamic_pointer_cast<AssetVersionImpl>(versionIter->second);
        assert(version);
        return version;
      }
      assert(false);
      return nullptr;
    }
  public:
    void AddVersion(const AssetKey & key, const MockVersion & v) {
      versions[key] = make_shared<MockVersion>(v);
    }
    virtual AssetHandle<const AssetVersionImpl> Get(const AssetKey &ref) {
      return AssetHandle<const AssetVersionImpl>(GetFromMap(ref), nullptr);
    }
    virtual AssetHandle<AssetVersionImpl> GetMutable(const AssetKey &ref) {
      auto handle = AssetHandle<AssetVersionImpl>(GetFromMap(ref), nullptr);
      dynamic_cast<MockVersion*>(handle.operator->())->loadedMutable = true;
      return handle;
    }
    void ResetLoadedMutable() {
      for (auto & v : versions) {
        v.second->loadedMutable = false;
      }
    }
};

class StateUpdaterTest : public testing::Test {
  protected:
   MockStorageManager sm;
   StateUpdater updater;
  public:
   StateUpdaterTest() : sm(), updater(&sm) {}
};

void SetVersions(MockStorageManager & sm, vector<MockVersion> versions) {
  for (auto & version: versions) {
    sm.AddVersion(version.name, version);
  }
}

AssetHandle<const MockVersion> GetVersion(MockStorageManager & sm, const AssetKey & key) {
  return sm.Get(fix(key));
}

AssetHandle<MockVersion> GetMutableVersion(MockStorageManager & sm, const AssetKey & key) {
  return sm.GetMutable(fix(key));
}

void SetParentChild(MockStorageManager & sm, AssetKey parent, AssetKey child) {
  GetMutableVersion(sm, parent)->children.push_back(fix(child));
  GetMutableVersion(sm, child)->parents.push_back(fix(parent));
}

void SetListenerInput(MockStorageManager & sm, AssetKey listener, AssetKey input) {
  GetMutableVersion(sm, listener)->inputs.push_back(fix(input));
  GetMutableVersion(sm, input)->listeners.push_back(fix(listener));
}

void SetDependent(MockStorageManager & sm, AssetKey dependee, AssetKey dependent) {
  GetMutableVersion(sm, dependee)->dependents.push_back(fix(dependent));
}

void GetBigTree(MockStorageManager & sm) {
  SetVersions(sm,
              {
                MockVersion("gp"),
                MockVersion("p1"),
                MockVersion("p2"),
                MockVersion("gpi"),
                MockVersion("pi1"),
                MockVersion("c1"),
                MockVersion("c2"),
                MockVersion("c3"),
                MockVersion("c4"),
                MockVersion("ci1"),
                MockVersion("ci2"),
                MockVersion("ci3")
              });
  SetParentChild(sm, "gp", "p1");
  SetParentChild(sm, "gp", "p2");
  SetListenerInput(sm, "gp", "gpi");
  SetListenerInput(sm, "p1", "pi1");
  SetParentChild(sm, "p1", "c1");
  SetParentChild(sm, "p1", "c2");
  SetParentChild(sm, "p2", "c3");
  SetParentChild(sm, "p2", "c4");
  SetListenerInput(sm, "c2", "ci3");
  SetListenerInput(sm, "c4", "ci1");
  SetListenerInput(sm, "c4", "ci2");
  SetDependent(sm, "gp", "p1");
  SetDependent(sm, "gp", "c2");
  SetDependent(sm, "p1", "c1");
}

TEST_F(StateUpdaterTest, SetStateSingleVersion) {
  AssetKey ref1 = "test1";
  AssetKey ref2 = "test2";
  AssetKey ref3 = "test3";
  SetVersions(sm, {MockVersion(ref1), MockVersion(ref2), MockVersion(ref3)});
  updater.SetStateForRefAndDependents(fix(ref1), AssetDefs::Bad, [](AssetDefs::State) { return true; });
  ASSERT_TRUE(GetVersion(sm, ref1)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
  updater.SetStateForRefAndDependents(fix(ref2), AssetDefs::Bad, [](AssetDefs::State) { return false; });
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersions) {
  GetBigTree(sm);
  updater.SetStateForRefAndDependents(fix("gp"), AssetDefs::Canceled, [](AssetDefs::State) {return true; });

  ASSERT_TRUE(GetVersion(sm, "gp")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c2")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gp")->notificationsSent);
  ASSERT_FALSE(GetVersion(sm, "p1")->notificationsSent);
  ASSERT_FALSE(GetVersion(sm, "c1")->notificationsSent);
  ASSERT_FALSE(GetVersion(sm, "c2")->notificationsSent);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersionsFromChild) {
  GetBigTree(sm);
  sm.ResetLoadedMutable();
  updater.SetStateForRefAndDependents(fix("p1"), AssetDefs::Canceled, [](AssetDefs::State) { return true; });

  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gp")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);

  ASSERT_TRUE(GetVersion(sm, "p1")->loadedMutable);
  ASSERT_TRUE(GetVersion(sm, "c1")->loadedMutable);

  ASSERT_FALSE(GetVersion(sm, "gp")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "gpi")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "pi1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c3")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c4")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci3")->loadedMutable);
}

TEST_F(StateUpdaterTest, SetState_StateDoesAndDoesntChange) {
  SetVersions(sm, {MockVersion("a"), MockVersion("b")});
  GetMutableVersion(sm, "a")->state = CALCULATED_STATE;
  GetMutableVersion(sm, "b")->state = STARTING_STATE;

  updater.SetStateForRefAndDependents(fix("a"), CALCULATED_STATE, [](AssetDefs::State) { return true; });
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "b")->stateSet);

  updater.SetStateForRefAndDependents(fix("b"), CALCULATED_STATE, [](AssetDefs::State) { return true; });
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "b")->stateSet);
}

TEST_F(StateUpdaterTest, SetStatePredicate) {
  SetVersions(sm, {MockVersion("a"), MockVersion("b")});
  GetMutableVersion(sm, "a")->state = AssetDefs::Bad;
  GetMutableVersion(sm, "b")->state = AssetDefs::Canceled;
  SetDependent(sm, "a", "b");
  updater.SetStateForRefAndDependents(fix("a"), AssetDefs::Succeeded, [](AssetDefs::State state) {
    return state != AssetDefs::Canceled;
  });
  ASSERT_TRUE(GetVersion(sm, "a")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "b")->stateSet);
}

TEST_F(StateUpdaterTest, NeedComputeStateFalse) {
  SetVersions(sm, {MockVersion("a")});
  updater.SetStateForRefAndDependents(fix("a"), AssetDefs::Canceled, [](AssetDefs::State) { return true; });
  GetMutableVersion(sm, "a")->needComputeState = false;
  GetMutableVersion(sm, "a")->stateSet = false;
  updater.RecalculateAndSaveStates();
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  GetMutableVersion(sm, "a")->needComputeState = true;
  updater.RecalculateAndSaveStates();
  ASSERT_TRUE(GetVersion(sm, "a")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "a")->notificationsSent);
}

TEST_F(StateUpdaterTest, RecalculateState_StateDoesAndDoesntChange) {
  SetVersions(sm, {MockVersion("a"), MockVersion("b")});
  GetMutableVersion(sm, "a")->state = CALCULATED_STATE;
  GetMutableVersion(sm, "b")->state = STARTING_STATE;
  SetListenerInput(sm, "a", "b");
  updater.SetStateForRefAndDependents(fix("a"), CALCULATED_STATE, [](AssetDefs::State) { return false; });

  // Verify that the setup is correct
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "b")->stateSet);

  updater.RecalculateAndSaveStates();
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "b")->stateSet);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc,argv);
  return RUN_ALL_TESTS();
}
<commit_msg>Make sure assets aren't unnecessarily loaded as mutable<commit_after>/*
 * Copyright 2019 The Open GEE Contributors
 *
 * 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 "AssetVersion.h"
#include "gee_version.h"
#include "StateUpdater.h"

#include <algorithm>
#include <assert.h>
#include <gtest/gtest.h>
#include <map>
#include <string>

using namespace std;

// All refs must end with "?version=X" or the calls to AssetVersionRef::Bind
// will try to load assets from disk. For simplicity, we force everything to
// be version 1. When you write additional tests you have to remember to call
// this function when working directly with the state updater.
const string SUFFIX = "?version=1";
AssetKey fix(AssetKey ref) {
  return ref.toString() + SUFFIX;
}

const AssetDefs::State STARTING_STATE = AssetDefs::Blocked;
const AssetDefs::State CALCULATED_STATE = AssetDefs::InProgress;

class MockVersion : public AssetVersionImpl {
  public:
    bool stateSet;
    bool loadedMutable;
    bool notificationsSent;
    bool needComputeState;
    vector<AssetKey> dependents;

    MockVersion() : stateSet(false), loadedMutable(false), notificationsSent(false), needComputeState(true) {
      type = AssetDefs::Imagery;
      state = STARTING_STATE;
    }
    MockVersion(const AssetKey & ref)
        : MockVersion() {
      name = fix(ref);
    }
    MockVersion(const MockVersion & that) : MockVersion() {
      name = that.name; // Don't add the suffix - the other MockVersion already did
    }
    void DependentChildren(vector<SharedString> & d) const {
      for(auto dependent : dependents) {
        d.push_back(dependent);
      }
    }
    bool NeedComputeState() const { return needComputeState; }
    AssetDefs::State CalcStateByInputsAndChildren(
        AssetDefs::State stateByInputs,
        AssetDefs::State stateByChildren,
        bool blockersAreOffline,
        uint32 numWaitingFor) const {
      return CALCULATED_STATE;
    }
    void SetMyStateOnly(AssetDefs::State newState, bool sendNotifications) {
      stateSet = true;
      notificationsSent = sendNotifications;
    }

    // Not used - only included to make MockVersion non-virtual
    string PluginName(void) const { return string(); }
    void GetOutputFilenames(vector<string> &) const {}
    string GetOutputFilename(uint) const { return string(); }
};

using VersionMap = map<AssetKey, shared_ptr<MockVersion>>;

class MockStorageManager : public StorageManagerInterface<AssetVersionImpl> {
  private:
    VersionMap versions;
    PointerType GetFromMap(const AssetKey & ref) {
      auto versionIter = versions.find(ref);
      if (versionIter != versions.end()) {
        auto version = dynamic_pointer_cast<AssetVersionImpl>(versionIter->second);
        assert(version);
        return version;
      }
      assert(false);
      return nullptr;
    }
  public:
    void AddVersion(const AssetKey & key, const MockVersion & v) {
      versions[key] = make_shared<MockVersion>(v);
    }
    virtual AssetHandle<const AssetVersionImpl> Get(const AssetKey &ref) {
      return AssetHandle<const AssetVersionImpl>(GetFromMap(ref), nullptr);
    }
    virtual AssetHandle<AssetVersionImpl> GetMutable(const AssetKey &ref) {
      auto handle = AssetHandle<AssetVersionImpl>(GetFromMap(ref), nullptr);
      dynamic_cast<MockVersion*>(handle.operator->())->loadedMutable = true;
      return handle;
    }
    void ResetLoadedMutable() {
      for (auto & v : versions) {
        v.second->loadedMutable = false;
      }
    }
};

class StateUpdaterTest : public testing::Test {
  protected:
   MockStorageManager sm;
   StateUpdater updater;
  public:
   StateUpdaterTest() : sm(), updater(&sm) {}
};

void SetVersions(MockStorageManager & sm, vector<MockVersion> versions) {
  for (auto & version: versions) {
    sm.AddVersion(version.name, version);
  }
}

AssetHandle<const MockVersion> GetVersion(MockStorageManager & sm, const AssetKey & key) {
  return sm.Get(fix(key));
}

AssetHandle<MockVersion> GetMutableVersion(MockStorageManager & sm, const AssetKey & key) {
  return sm.GetMutable(fix(key));
}

void SetParentChild(MockStorageManager & sm, AssetKey parent, AssetKey child) {
  GetMutableVersion(sm, parent)->children.push_back(fix(child));
  GetMutableVersion(sm, child)->parents.push_back(fix(parent));
}

void SetListenerInput(MockStorageManager & sm, AssetKey listener, AssetKey input) {
  GetMutableVersion(sm, listener)->inputs.push_back(fix(input));
  GetMutableVersion(sm, input)->listeners.push_back(fix(listener));
}

void SetDependent(MockStorageManager & sm, AssetKey dependee, AssetKey dependent) {
  GetMutableVersion(sm, dependee)->dependents.push_back(fix(dependent));
}

void GetBigTree(MockStorageManager & sm) {
  SetVersions(sm,
              {
                MockVersion("gp"),
                MockVersion("p1"),
                MockVersion("p2"),
                MockVersion("gpi"),
                MockVersion("pi1"),
                MockVersion("c1"),
                MockVersion("c2"),
                MockVersion("c3"),
                MockVersion("c4"),
                MockVersion("ci1"),
                MockVersion("ci2"),
                MockVersion("ci3")
              });
  SetParentChild(sm, "gp", "p1");
  SetParentChild(sm, "gp", "p2");
  SetListenerInput(sm, "gp", "gpi");
  SetListenerInput(sm, "p1", "pi1");
  SetParentChild(sm, "p1", "c1");
  SetParentChild(sm, "p1", "c2");
  SetParentChild(sm, "p2", "c3");
  SetParentChild(sm, "p2", "c4");
  SetListenerInput(sm, "c2", "ci3");
  SetListenerInput(sm, "c4", "ci1");
  SetListenerInput(sm, "c4", "ci2");
  SetDependent(sm, "gp", "p1");
  SetDependent(sm, "gp", "c2");
  SetDependent(sm, "p1", "c1");
}

TEST_F(StateUpdaterTest, SetStateSingleVersion) {
  AssetKey ref1 = "test1";
  AssetKey ref2 = "test2";
  AssetKey ref3 = "test3";
  SetVersions(sm, {MockVersion(ref1), MockVersion(ref2), MockVersion(ref3)});
  updater.SetStateForRefAndDependents(fix(ref1), AssetDefs::Bad, [](AssetDefs::State) { return true; });
  ASSERT_TRUE(GetVersion(sm, ref1)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
  updater.SetStateForRefAndDependents(fix(ref2), AssetDefs::Bad, [](AssetDefs::State) { return false; });
  ASSERT_FALSE(GetVersion(sm, ref2)->stateSet);
  ASSERT_FALSE(GetVersion(sm, ref3)->stateSet);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersions) {
  GetBigTree(sm);
  updater.SetStateForRefAndDependents(fix("gp"), AssetDefs::Canceled, [](AssetDefs::State) {return true; });

  ASSERT_TRUE(GetVersion(sm, "gp")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c2")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gp")->notificationsSent);
  ASSERT_FALSE(GetVersion(sm, "p1")->notificationsSent);
  ASSERT_FALSE(GetVersion(sm, "c1")->notificationsSent);
  ASSERT_FALSE(GetVersion(sm, "c2")->notificationsSent);
}

TEST_F(StateUpdaterTest, SetStateMultipleVersionsFromChild) {
  GetBigTree(sm);
  sm.ResetLoadedMutable();
  updater.SetStateForRefAndDependents(fix("p1"), AssetDefs::Canceled, [](AssetDefs::State) { return true; });

  ASSERT_TRUE(GetVersion(sm, "p1")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "c1")->stateSet);

  ASSERT_FALSE(GetVersion(sm, "gp")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "gpi")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "pi1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c3")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "c4")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci1")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci2")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "ci3")->stateSet);

  ASSERT_TRUE(GetVersion(sm, "p1")->loadedMutable);
  ASSERT_TRUE(GetVersion(sm, "c1")->loadedMutable);

  ASSERT_FALSE(GetVersion(sm, "gp")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "gpi")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "pi1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c3")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "c4")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci1")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci2")->loadedMutable);
  ASSERT_FALSE(GetVersion(sm, "ci3")->loadedMutable);
}

TEST_F(StateUpdaterTest, SetState_StateDoesAndDoesntChange) {
  SetVersions(sm, {MockVersion("a"), MockVersion("b")});
  GetMutableVersion(sm, "a")->state = CALCULATED_STATE;
  GetMutableVersion(sm, "b")->state = STARTING_STATE;

  updater.SetStateForRefAndDependents(fix("a"), CALCULATED_STATE, [](AssetDefs::State) { return true; });
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "b")->stateSet);

  updater.SetStateForRefAndDependents(fix("b"), CALCULATED_STATE, [](AssetDefs::State) { return true; });
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "b")->stateSet);
}

TEST_F(StateUpdaterTest, SetStatePredicate) {
  SetVersions(sm, {MockVersion("a"), MockVersion("b")});
  GetMutableVersion(sm, "a")->state = AssetDefs::Bad;
  GetMutableVersion(sm, "b")->state = AssetDefs::Canceled;
  SetDependent(sm, "a", "b");
  updater.SetStateForRefAndDependents(fix("a"), AssetDefs::Succeeded, [](AssetDefs::State state) {
    return state != AssetDefs::Canceled;
  });
  ASSERT_TRUE(GetVersion(sm, "a")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "b")->stateSet);
}

TEST_F(StateUpdaterTest, NeedComputeStateFalse) {
  SetVersions(sm, {MockVersion("a")});
  updater.SetStateForRefAndDependents(fix("a"), AssetDefs::Canceled, [](AssetDefs::State) { return true; });
  GetMutableVersion(sm, "a")->needComputeState = false;
  GetMutableVersion(sm, "a")->stateSet = false;
  updater.RecalculateAndSaveStates();
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  GetMutableVersion(sm, "a")->needComputeState = true;
  updater.RecalculateAndSaveStates();
  ASSERT_TRUE(GetVersion(sm, "a")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "a")->notificationsSent);
}

TEST_F(StateUpdaterTest, RecalculateState_StateDoesAndDoesntChange) {
  SetVersions(sm, {MockVersion("a"), MockVersion("b")});
  GetMutableVersion(sm, "a")->state = CALCULATED_STATE;
  GetMutableVersion(sm, "b")->state = STARTING_STATE;
  SetListenerInput(sm, "a", "b");
  GetMutableVersion(sm, "a")->loadedMutable = false;
  updater.SetStateForRefAndDependents(fix("a"), CALCULATED_STATE, [](AssetDefs::State) { return false; });

  // Verify that the setup is correct
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "b")->stateSet);

  updater.RecalculateAndSaveStates();
  ASSERT_FALSE(GetVersion(sm, "a")->stateSet);
  ASSERT_TRUE(GetVersion(sm, "b")->stateSet);
  ASSERT_FALSE(GetVersion(sm, "a")->loadedMutable);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc,argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <OpenColorIO/OpenColorIO.h>

#include "MatrixOps.h"
#include "MathUtils.h"

#include <cmath>
#include <cstring>
#include <sstream>

OCIO_NAMESPACE_ENTER
{
    namespace
    {
        void ApplyClampExponent(float* rgbaBuffer, long numPixels,
                                const float* exp4)
        {
            for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
            {
                rgbaBuffer[0] = powf( std::max(0.0f, rgbaBuffer[0]), exp4[0]);
                rgbaBuffer[1] = powf( std::max(0.0f, rgbaBuffer[1]), exp4[1]);
                rgbaBuffer[2] = powf( std::max(0.0f, rgbaBuffer[2]), exp4[2]);
                rgbaBuffer[3] = powf( std::max(0.0f, rgbaBuffer[3]), exp4[3]);

                rgbaBuffer += 4;
            }
        }

        const int FLOAT_DECIMALS = 7;
    }

    namespace
    {
        class ExponentOp : public Op
        {
        public:
            ExponentOp(const float * exp4,
                       TransformDirection direction);
            virtual ~ExponentOp();

            virtual OpRcPtr clone() const;

            virtual std::string getInfo() const;
            virtual std::string getCacheID() const;

            virtual bool isNoOp() const;
            virtual void finalize();
            virtual void apply(float* rgbaBuffer, long numPixels) const;

            virtual bool supportsGpuShader() const;
            virtual void writeGpuShader(std::ostringstream & shader,
                                        const std::string & pixelName,
                                        const GpuShaderDesc & shaderDesc) const;

            virtual bool definesGpuAllocation() const;
            virtual GpuAllocationData getGpuAllocation() const;

        private:
            float m_exp4[4];
            float m_finalExp4[4];
            TransformDirection m_direction;
            std::string m_cacheID;
        };

        ExponentOp::ExponentOp(const float * exp4,
                               TransformDirection direction):
                               Op(),
                               m_direction(direction)
        {
            memcpy(m_exp4, exp4, 4*sizeof(float));
            memset(m_finalExp4, 0, 4*sizeof(float));
        }

        OpRcPtr ExponentOp::clone() const
        {
            OpRcPtr op = OpRcPtr(new ExponentOp(m_exp4, m_direction));
            return op;
        }

        ExponentOp::~ExponentOp()
        { }

        std::string ExponentOp::getInfo() const
        {
            return "<ExponentOp>";
        }

        std::string ExponentOp::getCacheID() const
        {
            return m_cacheID;
        }

        // TODO: compute real value for isNoOp
        bool ExponentOp::isNoOp() const
        {
            return false;
        }

        void ExponentOp::finalize()
        {
            if(m_direction == TRANSFORM_DIR_UNKNOWN)
            {
                throw Exception("Cannot apply ExponentOp op, unspecified transform direction.");
            }

            if(m_direction == TRANSFORM_DIR_INVERSE)
            {
                for(int i=0; i<4; ++i)
                {
                    if(!IsScalarEqualToZero(m_exp4[i]))
                    {
                        m_finalExp4[i] = 1.0f / m_exp4[i];
                    }
                    else
                    {
                        throw Exception("Cannot apply ExponentOp op, Cannot apply 0.0 exponent in the inverse.");
                    }
                }
            }
            else
            {
                memcpy(m_finalExp4, m_exp4, 4*sizeof(float));
            }

            // Create the cacheID
            std::ostringstream cacheIDStream;
            cacheIDStream << "<ExponentOp ";
            cacheIDStream.precision(FLOAT_DECIMALS);
            for(int i=0; i<4; ++i)
            {
                cacheIDStream << m_finalExp4[i] << " ";
            }
            cacheIDStream << ">";
            m_cacheID = cacheIDStream.str();
        }

        void ExponentOp::apply(float* rgbaBuffer, long numPixels) const
        {
            if(!rgbaBuffer) return;

            ApplyClampExponent(rgbaBuffer, numPixels, m_finalExp4);
        }

        bool ExponentOp::supportsGpuShader() const
        {
            return false;
        }

        void ExponentOp::writeGpuShader(std::ostringstream & /*shader*/,
                                        const std::string & /*pixelName*/,
                                        const GpuShaderDesc & /*shaderDesc*/) const
        {
            // TODO: Add Gpu Shader for exponent op
            throw Exception("ExponentOp does not support analytical shader generation.");
        }

        bool ExponentOp::definesGpuAllocation() const
        {
            return false;
        }

        GpuAllocationData ExponentOp::getGpuAllocation() const
        {
            throw Exception("ExponentOp does not define a Gpu Allocation.");
        }

    }  // Anon namespace

    void CreateExponentOp(OpRcPtrVec & ops,
                          const float * exp4,
                          TransformDirection direction)
    {
        bool expIsIdentity = IsVecEqualToOne(exp4, 4);
        if(expIsIdentity) return;

        ops.push_back( OpRcPtr(new ExponentOp(exp4, direction)) );
    }
}
OCIO_NAMESPACE_EXIT
<commit_msg>exponentop implements gpu path<commit_after>/*
Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <cmath>
#include <cstring>
#include <sstream>

#include <OpenColorIO/OpenColorIO.h>

#include "GpuShaderUtils.h"
#include "MatrixOps.h"
#include "MathUtils.h"

OCIO_NAMESPACE_ENTER
{
    namespace
    {
        void ApplyClampExponent(float* rgbaBuffer, long numPixels,
                                const float* exp4)
        {
            for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
            {
                rgbaBuffer[0] = powf( std::max(0.0f, rgbaBuffer[0]), exp4[0]);
                rgbaBuffer[1] = powf( std::max(0.0f, rgbaBuffer[1]), exp4[1]);
                rgbaBuffer[2] = powf( std::max(0.0f, rgbaBuffer[2]), exp4[2]);
                rgbaBuffer[3] = powf( std::max(0.0f, rgbaBuffer[3]), exp4[3]);

                rgbaBuffer += 4;
            }
        }

        const int FLOAT_DECIMALS = 7;
    }

    namespace
    {
        class ExponentOp : public Op
        {
        public:
            ExponentOp(const float * exp4,
                       TransformDirection direction);
            virtual ~ExponentOp();

            virtual OpRcPtr clone() const;

            virtual std::string getInfo() const;
            virtual std::string getCacheID() const;

            virtual bool isNoOp() const;
            virtual void finalize();
            virtual void apply(float* rgbaBuffer, long numPixels) const;

            virtual bool supportsGpuShader() const;
            virtual void writeGpuShader(std::ostringstream & shader,
                                        const std::string & pixelName,
                                        const GpuShaderDesc & shaderDesc) const;

            virtual bool definesGpuAllocation() const;
            virtual GpuAllocationData getGpuAllocation() const;

        private:
            float m_exp4[4];
            float m_finalExp4[4];
            TransformDirection m_direction;
            std::string m_cacheID;
        };

        ExponentOp::ExponentOp(const float * exp4,
                               TransformDirection direction):
                               Op(),
                               m_direction(direction)
        {
            memcpy(m_exp4, exp4, 4*sizeof(float));
            memset(m_finalExp4, 0, 4*sizeof(float));
        }

        OpRcPtr ExponentOp::clone() const
        {
            OpRcPtr op = OpRcPtr(new ExponentOp(m_exp4, m_direction));
            return op;
        }

        ExponentOp::~ExponentOp()
        { }

        std::string ExponentOp::getInfo() const
        {
            return "<ExponentOp>";
        }

        std::string ExponentOp::getCacheID() const
        {
            return m_cacheID;
        }

        // TODO: compute real value for isNoOp
        bool ExponentOp::isNoOp() const
        {
            return false;
        }

        void ExponentOp::finalize()
        {
            if(m_direction == TRANSFORM_DIR_UNKNOWN)
            {
                throw Exception("Cannot apply ExponentOp op, unspecified transform direction.");
            }

            if(m_direction == TRANSFORM_DIR_INVERSE)
            {
                for(int i=0; i<4; ++i)
                {
                    if(!IsScalarEqualToZero(m_exp4[i]))
                    {
                        m_finalExp4[i] = 1.0f / m_exp4[i];
                    }
                    else
                    {
                        throw Exception("Cannot apply ExponentOp op, Cannot apply 0.0 exponent in the inverse.");
                    }
                }
            }
            else
            {
                memcpy(m_finalExp4, m_exp4, 4*sizeof(float));
            }

            // Create the cacheID
            std::ostringstream cacheIDStream;
            cacheIDStream << "<ExponentOp ";
            cacheIDStream.precision(FLOAT_DECIMALS);
            for(int i=0; i<4; ++i)
            {
                cacheIDStream << m_finalExp4[i] << " ";
            }
            cacheIDStream << ">";
            m_cacheID = cacheIDStream.str();
        }

        void ExponentOp::apply(float* rgbaBuffer, long numPixels) const
        {
            if(!rgbaBuffer) return;

            ApplyClampExponent(rgbaBuffer, numPixels, m_finalExp4);
        }

        bool ExponentOp::supportsGpuShader() const
        {
            return true;
        }

        void ExponentOp::writeGpuShader(std::ostringstream & shader,
                                        const std::string & pixelName,
                                        const GpuShaderDesc & shaderDesc) const
        {
            GpuLanguage lang = shaderDesc.getLanguage();
            float zerovec[4] = { 0.0f, 0.0f, 0.0f, 0.0f };

            shader << pixelName << " = pow(";
            shader << "max(" << pixelName << ", " << GpuTextHalf4(zerovec, lang) << ")";
            shader << ", " << GpuTextHalf4(m_finalExp4, lang) << ");\n";
        }

        bool ExponentOp::definesGpuAllocation() const
        {
            return false;
        }

        GpuAllocationData ExponentOp::getGpuAllocation() const
        {
            throw Exception("ExponentOp does not define a Gpu Allocation.");
        }

    }  // Anon namespace

    void CreateExponentOp(OpRcPtrVec & ops,
                          const float * exp4,
                          TransformDirection direction)
    {
        bool expIsIdentity = IsVecEqualToOne(exp4, 4);
        if(expIsIdentity) return;

        ops.push_back( OpRcPtr(new ExponentOp(exp4, direction)) );
    }
}
OCIO_NAMESPACE_EXIT
<|endoftext|>