Skip to content

Commit 8bf5e5a

Browse files
committed
Valve: disco ball - Handle close command
1 parent 02906e1 commit 8bf5e5a

File tree

3 files changed

+235
-13
lines changed

3 files changed

+235
-13
lines changed

src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -454,9 +454,15 @@ CHIP_ERROR ClusterLogic::HandleOpenCommand(std::optional<DataModel::Nullable<Ela
454454
return CHIP_NO_ERROR;
455455
}
456456

457-
void ClusterLogic::HandleCloseInternal()
457+
CHIP_ERROR ClusterLogic::HandleCloseCommand()
458+
{
459+
VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
460+
DeviceLayer::SystemLayer().CancelTimer(HandleUpdateRemainingDuration, this);
461+
return HandleCloseInternal();
462+
}
463+
464+
CHIP_ERROR ClusterLogic::HandleCloseInternal()
458465
{
459-
// TODO: call the delegate and add to tests
460466
CHIP_ERROR err;
461467
BitMask<ValveFaultBitmap> faults;
462468
if (mConformance.HasFeature(Feature::kLevel))
@@ -497,6 +503,7 @@ void ClusterLogic::HandleCloseInternal()
497503
mState.SetTargetLevel(DataModel::NullNullable);
498504
mState.SetTargetState(DataModel::NullNullable);
499505
mState.SetAutoCloseTime(DataModel::NullNullable);
506+
return err;
500507
}
501508

502509
void ClusterLogic::HandleUpdateRemainingDuration(System::Layer * systemLayer, void * context)

src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,16 @@ class ClusterLogic
170170
// Current state
171171
// Current level
172172

173-
// Return CHIP_ERROR_INCORRECT_STATE if the class has not been initialized.
174-
// Return CHIP_ERROR_INVALID_ARGUMENT if the input values are out is out of range or the targetLevel is supplied when LVL is not
175-
// supported.
173+
// Returns CHIP_ERROR_INCORRECT_STATE if the class has not been initialized.
174+
// Returns CHIP_ERROR_INVALID_ARGUMENT if the input values are out is out of range or the targetLevel is supplied when LVL is
175+
// not supported.
176176
// Calls delegate HandleOpen function after validating the parameters
177177
CHIP_ERROR HandleOpenCommand(std::optional<DataModel::Nullable<ElapsedS>> openDuration, std::optional<Percent> targetLevel);
178178

179+
// Returns CHIP_ERROR_INCORRECT_STATE if the class has not been initialized.
180+
// Calls delegate HandleClose function after validating the parameters and stops any open duration timers.
181+
CHIP_ERROR HandleCloseCommand();
182+
179183
private:
180184
// Determines if the level value is allowed per the level step.
181185
bool ValueCompliesWithLevelStep(const uint8_t value);
@@ -193,7 +197,7 @@ class ClusterLogic
193197
void HandleUpdateRemainingDurationInternal();
194198
// Internal function called by HandleUpdateRemainingDuration to call the close function in the delegate and
195199
// set all the attributes back to their closed state.
196-
void HandleCloseInternal();
200+
CHIP_ERROR HandleCloseInternal();
197201

198202
bool mInitialized = false;
199203
System::Clock::Milliseconds64 mDurationStarted = System::Clock::Milliseconds64(0);

src/app/tests/TestValveConfigurationAndControl.cpp

+218-7
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace chip {
3232

3333
namespace System {
3434

35-
// TODO: This might be worthwhile to generalize
35+
// TODO: This might be worthwhile to generalize and put into the system layer, but it too will need unit tests.
3636
class TimerAndMockClock : public Clock::Internal::MockClock, public Layer
3737
{
3838
public:
@@ -268,7 +268,6 @@ class TestDelegateNoLevel : public NonLevelControlDelegate
268268
ValveStateEnum target = ValveStateEnum::kUnknownEnumValue;
269269
};
270270

271-
// TODO: This also might be good to generalize, by using a common matter context for each cluster with allowed overrides
272271
class MockedMatterContext : public MatterContext
273272
{
274273
public:
@@ -1947,9 +1946,222 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurati
19471946
//=========================================================================================
19481947
// Tests for handling close commands
19491948
//=========================================================================================
1950-
// while remaining duration is null and valve is open
19511949
// while remaining duration is not null and valve is open
1950+
1951+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveDurationLevel)
1952+
{
1953+
TestDelegateLevel delegate;
1954+
TestPersistentStorageDelegate storageDelegate;
1955+
EndpointId endpoint = 0;
1956+
MockedMatterContext context(endpoint, storageDelegate);
1957+
ClusterLogic logic(delegate, context);
1958+
1959+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync),
1960+
.supportsDefaultOpenLevel = true,
1961+
.supportsValveFault = true,
1962+
.supportsLevelStep = true };
1963+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
1964+
1965+
gSystemLayerAndClock.SetMonotonic(0_ms64);
1966+
gSystemLayerAndClock.Clear();
1967+
DataModel::Nullable<ElapsedS> openDuration;
1968+
openDuration.SetNonNull(2u);
1969+
EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR);
1970+
EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id));
1971+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 0);
1972+
1973+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
1974+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR);
1975+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
1976+
1977+
// Ensure the timer was cancelled
1978+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
1979+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
1980+
}
1981+
1982+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveDurationNoLevel)
1983+
{
1984+
TestDelegateNoLevel delegate;
1985+
TestPersistentStorageDelegate storageDelegate;
1986+
EndpointId endpoint = 0;
1987+
MockedMatterContext context(endpoint, storageDelegate);
1988+
ClusterLogic logic(delegate, context);
1989+
1990+
ClusterConformance conformance = {
1991+
.featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false
1992+
};
1993+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
1994+
1995+
gSystemLayerAndClock.SetMonotonic(0_ms64);
1996+
gSystemLayerAndClock.Clear();
1997+
DataModel::Nullable<ElapsedS> openDuration;
1998+
openDuration.SetNonNull(2u);
1999+
EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR);
2000+
EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id));
2001+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 0);
2002+
2003+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
2004+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR);
2005+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2006+
2007+
// Ensure the timer was cancelled
2008+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
2009+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2010+
}
2011+
2012+
// while remaining duration is null and valve is open
2013+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveNoDurationLevel)
2014+
{
2015+
TestDelegateLevel delegate;
2016+
TestPersistentStorageDelegate storageDelegate;
2017+
EndpointId endpoint = 0;
2018+
MockedMatterContext context(endpoint, storageDelegate);
2019+
ClusterLogic logic(delegate, context);
2020+
2021+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync),
2022+
.supportsDefaultOpenLevel = true,
2023+
.supportsValveFault = true,
2024+
.supportsLevelStep = true };
2025+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
2026+
2027+
gSystemLayerAndClock.SetMonotonic(0_ms64);
2028+
gSystemLayerAndClock.Clear();
2029+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
2030+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 0);
2031+
2032+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
2033+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR);
2034+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2035+
}
2036+
2037+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveNoDurationNoLevel)
2038+
{
2039+
TestDelegateNoLevel delegate;
2040+
TestPersistentStorageDelegate storageDelegate;
2041+
EndpointId endpoint = 0;
2042+
MockedMatterContext context(endpoint, storageDelegate);
2043+
ClusterLogic logic(delegate, context);
2044+
2045+
ClusterConformance conformance = {
2046+
.featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false
2047+
};
2048+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
2049+
2050+
gSystemLayerAndClock.SetMonotonic(0_ms64);
2051+
gSystemLayerAndClock.Clear();
2052+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
2053+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 0);
2054+
2055+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
2056+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR);
2057+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2058+
}
2059+
19522060
// while valve is closed
2061+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandClosedLevel)
2062+
{
2063+
TestDelegateLevel delegate;
2064+
TestPersistentStorageDelegate storageDelegate;
2065+
EndpointId endpoint = 0;
2066+
MockedMatterContext context(endpoint, storageDelegate);
2067+
ClusterLogic logic(delegate, context);
2068+
2069+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync),
2070+
.supportsDefaultOpenLevel = true,
2071+
.supportsValveFault = true,
2072+
.supportsLevelStep = true };
2073+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
2074+
2075+
gSystemLayerAndClock.SetMonotonic(0_ms64);
2076+
gSystemLayerAndClock.Clear();
2077+
2078+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR);
2079+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2080+
}
2081+
2082+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandClosedNoLevel)
2083+
{
2084+
TestDelegateNoLevel delegate;
2085+
TestPersistentStorageDelegate storageDelegate;
2086+
EndpointId endpoint = 0;
2087+
MockedMatterContext context(endpoint, storageDelegate);
2088+
ClusterLogic logic(delegate, context);
2089+
2090+
ClusterConformance conformance = {
2091+
.featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false
2092+
};
2093+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
2094+
2095+
gSystemLayerAndClock.SetMonotonic(0_ms64);
2096+
gSystemLayerAndClock.Clear();
2097+
2098+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR);
2099+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2100+
}
2101+
2102+
// Errors when Init hasn't been called.
2103+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandClosedBeforeInit)
2104+
{
2105+
TestDelegateNoLevel delegate;
2106+
TestPersistentStorageDelegate storageDelegate;
2107+
EndpointId endpoint = 0;
2108+
MockedMatterContext context(endpoint, storageDelegate);
2109+
ClusterLogic logic(delegate, context);
2110+
2111+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_ERROR_INCORRECT_STATE);
2112+
}
2113+
2114+
// Error returns when delegate retuns error on close command
2115+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandReturnsErrorLevel)
2116+
{
2117+
TestDelegateLevel delegate;
2118+
TestPersistentStorageDelegate storageDelegate;
2119+
EndpointId endpoint = 0;
2120+
MockedMatterContext context(endpoint, storageDelegate);
2121+
ClusterLogic logic(delegate, context);
2122+
2123+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync),
2124+
.supportsDefaultOpenLevel = true,
2125+
.supportsValveFault = true,
2126+
.supportsLevelStep = true };
2127+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
2128+
2129+
delegate.simulateCloseFailure = true;
2130+
2131+
gSystemLayerAndClock.SetMonotonic(0_ms64);
2132+
gSystemLayerAndClock.Clear();
2133+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
2134+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 0);
2135+
2136+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
2137+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_ERROR_INTERNAL);
2138+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2139+
}
2140+
2141+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandReturnsErrorNoLevel)
2142+
{
2143+
TestDelegateNoLevel delegate;
2144+
TestPersistentStorageDelegate storageDelegate;
2145+
EndpointId endpoint = 0;
2146+
MockedMatterContext context(endpoint, storageDelegate);
2147+
ClusterLogic logic(delegate, context);
2148+
2149+
ClusterConformance conformance = {
2150+
.featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false
2151+
};
2152+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
2153+
2154+
delegate.simulateCloseFailure = true;
2155+
2156+
gSystemLayerAndClock.SetMonotonic(0_ms64);
2157+
gSystemLayerAndClock.Clear();
2158+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
2159+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 0);
2160+
2161+
gSystemLayerAndClock.AdvanceMonotonic(1000_ms64);
2162+
EXPECT_EQ(logic.HandleCloseCommand(), CHIP_ERROR_INTERNAL);
2163+
EXPECT_EQ(delegate.numHandleCloseValveCalls, 1);
2164+
}
19532165

19542166
//=========================================================================================
19552167
// Tests for timing for async read updates to current / target level and state
@@ -1972,10 +2184,9 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurati
19722184
//=========================================================================================
19732185
// Tests for attribute callbacks from delegates
19742186
//=========================================================================================
1975-
// TODO: Should the delegate call the cluster logic class direclty, or should this be piped through the delegate class so the app
1976-
// layer ONLY has to interact with the delegate?
1977-
// Test setter for valve fault Test attribute change notifications are sent out and not
1978-
// sent out when the attribute is change do the same value - add in prior
2187+
// TODO: Should the delegate call the cluster logic class direclty, or should this be piped through the delegate class so the
2188+
// app layer ONLY has to interact with the delegate? Test setter for valve fault Test attribute change notifications are sent
2189+
// out and not sent out when the attribute is change do the same value - add in prior
19792190

19802191
//=========================================================================================
19812192
// Tests for attribute callbacks from delegates

0 commit comments

Comments
 (0)