Skip to content
9 changes: 5 additions & 4 deletions model/sheet.cc
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ bool Sheet::contains(float x, float y) const

bool Sheet::scrollUp()
{
if (scrollOffset+scrollDelta<scrollMax)
if (scrollDelta && scrollOffset+scrollDelta<scrollMax)
{
scrollOffset+=scrollDelta;
setSliceIndicator();
Expand All @@ -143,7 +143,7 @@ bool Sheet::scrollUp()

bool Sheet::scrollDown()
{
if (scrollOffset>scrollDelta)
if (scrollDelta && scrollOffset>=scrollDelta)
{
scrollOffset-=scrollDelta;
setSliceIndicator();
Expand Down Expand Up @@ -172,13 +172,14 @@ namespace {
}
}

void Sheet::setSliceIndicator()
const string& Sheet::setSliceIndicator()
{
if (!value || value->rank()<=2) return;
if (!value || value->rank()<=2) return sliceIndicator="";
auto idx=value->hypercube().splitIndex(scrollOffset);
sliceIndicator=formattedStr(value->hypercube().xvectors[2], idx[2]);
for (size_t i=3; i<idx.size(); ++i)
sliceIndicator+=" | "+formattedStr(value->hypercube().xvectors[i], idx[i]);
return sliceIndicator;
}

namespace
Expand Down
3 changes: 2 additions & 1 deletion model/sheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ namespace minsky
bool scrollUp();
bool scrollDown();
bool onKeyPress(int keySym, const std::string& utf8, int state) override;
void setSliceIndicator();
/// @return sliceIndicator
const std::string& setSliceIndicator();

void draw(cairo_t* cairo) const override;

Expand Down
2 changes: 1 addition & 1 deletion test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LIBS:=$(subst -lecolab,$(ECOLAB_HOME)/lib/libecolab.a,$(LIBS))

VPATH= .. ../schema ../model ../engine ../RESTService ../RavelCAPI/civita ../RavelCAPI $(ECOLAB_HOME)/include

GTESTOBJS=testCSVParser.o testCanvas.o testDerivative.o testExpressionWalker.o testGrid.o testLatexToPango.o testLockGroup.o testMdl.o testMinsky.o testModel.o testPannableTab.o testPhillips.o testPlotWidget.o testPubTab.o testSaver.o testStr.o testTensorOps.o testUnits.o testUserFunction.o testVariable.o testVariablePane.o testXVector.o testZStream.o ticket-1461.o
GTESTOBJS=testCSVParser.o testCanvas.o testDerivative.o testExpressionWalker.o testGrid.o testLatexToPango.o testLockGroup.o testMdl.o testMinsky.o testModel.o testPannableTab.o testPhillips.o testPlotWidget.o testPubTab.o testSaver.o testSheet.o testStr.o testTensorOps.o testUnits.o testUserFunction.o testVariable.o testVariablePane.o testXVector.o testZStream.o ticket-1461.o

MINSKYOBJS=localMinsky.o ../libminsky.a
FLAGS:=-I.. -I../RESTService -I../RavelCAPI/civita -I../RavelCAPI $(FLAGS)
Expand Down
10 changes: 5 additions & 5 deletions test/testMinsky.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,7 @@ TEST(TensorOps, evalOpEvaluate)
EXPECT_EQ(0, variableValues.count(":saveVar"));

load(testFile);
EXPECT_EQ(model->items.size(), 1); // time + saveVar
EXPECT_EQ(model->items.size(), 1);
EXPECT_TRUE(variableValues.count(":saveVar") > 0);

remove(testFile.c_str());
Expand All @@ -1677,12 +1677,12 @@ TEST(TensorOps, evalOpEvaluate)

insertGroupFromFile(groupFile);

// model->items should be empty
// model->items should be empty, as the model has been cleared
EXPECT_EQ(0, model->items.size());
// model->groups should contain one group
// model->groups should contain the one group that has been imported from the file
EXPECT_EQ(1, model->groups.size());
if (model->groups.size() > 0) {
// which intern contains one item in model->groups[0]->items
// which in turn contains one item in model->groups[0]->items
EXPECT_EQ(1, model->groups[0]->items.size());
}

Expand Down Expand Up @@ -1752,7 +1752,7 @@ TEST(TensorOps, evalOpEvaluate)
EXPECT_GT(integrals.size(), 0);

// stash the correct sizes
auto fvSz=flowVars.size(), stSz=stockVars.size(), eqSz=stockVars.size(), intSz=integrals.size();
auto fvSz=flowVars.size(), stSz=stockVars.size(), eqSz=equations.size(), intSz=integrals.size();
// add some rubbish at the end of all of these
flowVars.resize(fvSz+5);
stockVars.resize(stSz+5);
Expand Down
193 changes: 193 additions & 0 deletions test/testSheet.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
@copyright Steve Keen 2024
@author Russell Standish
This file is part of Minsky.

Minsky is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Minsky is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Minsky. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sheet.h"
#include "minsky.h"
#include "minsky_epilogue.h"
#include <gtest/gtest.h>
#include <boost/filesystem.hpp>

using namespace minsky;
using namespace std;

namespace
{
class SheetTest : public Sheet, public ::testing::Test
{
protected:
void SetUp() override
{
// Set default dimensions for tests
iWidth(200);
iHeight(150);
moveTo(100, 100);
}

void TearDown() override
{
// Clean up global minsky state after each test
minsky::minsky().clearAllMaps();
}
};

TEST_F(SheetTest, construction)
{
Sheet sheet;
EXPECT_EQ(1, sheet.portsSize());
EXPECT_EQ(100, sheet.iWidth());
EXPECT_EQ(100, sheet.iHeight());
}

TEST_F(SheetTest, onResizeHandle)
{
moveTo(100, 100);
iWidth(200);
iHeight(150);
double z = zoomFactor();
double w = 0.5 * 200 * z;
double h = 0.5 * 150 * z;

// Test bottom-right resize handle
EXPECT_TRUE(onResizeHandle(100 + w, 100 + h));

// Test top-right resize handle
EXPECT_TRUE(onResizeHandle(100 + w, 100 - h));

// Test bottom-left resize handle (when showRavel is false)
showRavel = false;
EXPECT_TRUE(onResizeHandle(100 - w, 100 + h));

// Test points not on resize handle
EXPECT_FALSE(onResizeHandle(100, 100));
}

TEST_F(SheetTest, inItem)
{
moveTo(100, 100);
iWidth(200);
iHeight(150);

// Test point inside item
EXPECT_TRUE(inItem(100, 100));

// Test point outside item
EXPECT_FALSE(inItem(0, 0));
EXPECT_FALSE(inItem(300, 300));
}

TEST_F(SheetTest, clickType)
{
moveTo(100, 100);
iWidth(200);
iHeight(150);
double z = zoomFactor();
double w = 0.5 * 200 * z;
double h = 0.5 * 150 * z;

// Test resize handle
EXPECT_EQ(ClickType::onResize, clickType(100 + w, 100 + h));

// Test inside item
EXPECT_EQ(ClickType::inItem, clickType(100, 100));

// Test on item border
EXPECT_EQ(ClickType::onItem, clickType(100 + w * 0.9, 100 + h * 0.9));

// Test outside item
EXPECT_EQ(ClickType::outside, clickType(0, 0));
}

TEST_F(SheetTest, contains)
{
moveTo(100, 100);
iWidth(200);
iHeight(150);

// Test point inside item
EXPECT_TRUE(contains(100, 100));

// Test point outside item
EXPECT_FALSE(contains(0, 0));
}

TEST_F(SheetTest, scrollUpDown)
{
// Initial state - no scrolling possible without data
EXPECT_FALSE(scrollUp());
EXPECT_FALSE(scrollDown());
// add a rank 3 tensor, which is "scrollable"
minsky::minsky().canvas.addVariable("3dTensor",VariableType::parameter);
auto& param=*minsky::minsky().canvas.itemFocus->variableCast();
param.init("rand(3,3,3)");
minsky::minsky().canvas.addSheet();
auto& sheet=dynamic_cast<Sheet&>(*minsky::minsky().canvas.itemFocus);
minsky::minsky().model->addWire(param, sheet, 0);
minsky::minsky().reset();
EXPECT_TRUE(sheet.scrollUp());
EXPECT_TRUE(sheet.scrollDown());
}

TEST_F(SheetTest, onKeyPress)
{
// Test arrow key handling with rank 3 tensor
minsky::minsky().canvas.addVariable("3dTensor",VariableType::parameter);
auto& param=*minsky::minsky().canvas.itemFocus->variableCast();
param.init("rand(3,3,3)");
minsky::minsky().canvas.addSheet();
auto& sheet=dynamic_cast<Sheet&>(*minsky::minsky().canvas.itemFocus);
minsky::minsky().model->addWire(param, sheet, 0);
minsky::minsky().reset();

// Up arrow (0xff52) - should scroll up
EXPECT_TRUE(sheet.onKeyPress(0xff52, "", 0));

// Down arrow (0xff54) - should scroll down
EXPECT_TRUE(sheet.onKeyPress(0xff54, "", 0));

// Right arrow (0xff53) - should scroll up
EXPECT_TRUE(sheet.onKeyPress(0xff53, "", 0));

// Left arrow (0xff51) - should scroll down
EXPECT_TRUE(sheet.onKeyPress(0xff51, "", 0));

// Other key - should return false
EXPECT_FALSE(sheet.onKeyPress(0x0041, "A", 0));
}

TEST_F(SheetTest, exportAsCSVWithoutValue)
{
// Attempting to export without a value should throw an error
string tmpFile = boost::filesystem::unique_path().string();
EXPECT_THROW(exportAsCSV(tmpFile, false), std::exception);
}

TEST_F(SheetTest, setSliceIndicator)
{
// Add a rank 3 tensor to test slice indicator
minsky::minsky().canvas.addVariable("3dTensor",VariableType::parameter);
auto& param=*minsky::minsky().canvas.itemFocus->variableCast();
param.init("rand(3,3,3)");
minsky::minsky().canvas.addSheet();
auto& sheet=dynamic_cast<Sheet&>(*minsky::minsky().canvas.itemFocus);
minsky::minsky().model->addWire(param, sheet, 0);
minsky::minsky().reset();

// Check that sliceIndicator is set to something useful
EXPECT_FALSE(sheet.setSliceIndicator().empty());
}
}
Loading