diff --git a/sc/qa/unit/ucalc_copypaste.cxx b/sc/qa/unit/ucalc_copypaste.cxx index e226e4ef6d1c..2bdde152b4a3 100644 --- a/sc/qa/unit/ucalc_copypaste.cxx +++ b/sc/qa/unit/ucalc_copypaste.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -10114,6 +10115,81 @@ CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyFromClipPartialTableNoPhantom) m_pDoc->DeleteTab(0); } +CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyFromClipCrossDocSameNameNoBind) +{ + // A full table copy pasted into a different document that already owns an + // unrelated table of the same name must not bind the pasted structured + // reference to that destination table. The name is document-global and + // cannot be recreated, so the reference is left unresolved (#REF!) instead + // of silently computing the wrong table's data. + m_pDoc->InsertTab(0, u"Src"_ustr); + + // Source MyTable A1:C4; a structured-ref formula in C2 reads its H1 + // column. + auto pTable = std::make_unique(u"MyTable"_ustr, 0, 0, 0, 2, 3, true, true); + pTable->SetTableColumnNames(std::vector{ u"H1"_ustr, u"H2"_ustr, u"H3"_ustr }); + CPPUNIT_ASSERT(m_pDoc->GetDBCollection()->getNamedDBs().insert(std::move(pTable))); + m_pDoc->SetValue(0, 1, 0, 1.0); // A2 + m_pDoc->SetValue(0, 2, 0, 4.0); // A3 + m_pDoc->SetValue(0, 3, 0, 16.0); // A4 + m_pDoc->SetString(2, 1, 0, u"=SUM(MyTable[H1])"_ustr); // C2 + m_pDoc->CalcAll(); + CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(2, 1, 0)); + + // Copy the whole table A1:C4. + ScRange aClipRange(0, 0, 0, 2, 3, 0); + ScClipParam aClipParam(aClipRange, false); + ScMarkData aMark(m_pDoc->GetSheetLimits()); + aMark.SelectOneTable(0); + aMark.SetMarkArea(aClipRange); + ScDocument aClipDoc(SCDOCMODE_CLIP); + m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false); + + // Destination document with its OWN unrelated MyTable at F11:H14 + // carrying distinct H1 data. + ScDocShellRef xDestDocSh = new ScDocShell; + xDestDocSh->DoLoad(new SfxMedium(u"file:///crossdocnameclash.fake"_ustr, StreamMode::STD_READWRITE)); + ScDocument& rDestDoc = xDestDocSh->GetDocument(); + rDestDoc.InsertTab(0, u"Dest"_ustr); + auto pDestTable = std::make_unique(u"MyTable"_ustr, 0, 5, 10, 7, 13, true, true); + pDestTable->SetTableColumnNames(std::vector{ u"H1"_ustr, u"H2"_ustr, u"H3"_ustr }); + CPPUNIT_ASSERT(rDestDoc.GetDBCollection()->getNamedDBs().insert(std::move(pDestTable))); + rDestDoc.SetValue(5, 11, 0, 100.0); // F12 + rDestDoc.SetValue(5, 12, 0, 200.0); // F13 + rDestDoc.SetValue(5, 13, 0, 400.0); // F14 + + // Paste the copied table at A1, clear of the destination's own table. + ScRange aDestRange(0, 0, 0, 2, 3, 0); + ScMarkData aDestMark(rDestDoc.GetSheetLimits()); + aDestMark.SelectOneTable(0); + rDestDoc.CopyFromClip(aDestRange, aDestMark, InsertDeleteFlags::ALL, nullptr, &aClipDoc); + rDestDoc.CalcAll(); + + // The pasted formula resolves to FormulaError::NoRef. + CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), + int(rDestDoc.GetErrCode(ScAddress(2, 1, 0)))); + + // The source's H1 data landed in the destination at A2:A4. + CPPUNIT_ASSERT_EQUAL(1.0, rDestDoc.GetValue(0, 1, 0)); + CPPUNIT_ASSERT_EQUAL(4.0, rDestDoc.GetValue(0, 2, 0)); + CPPUNIT_ASSERT_EQUAL(16.0, rDestDoc.GetValue(0, 3, 0)); + + // The destination's own table stays at its original area with its data + // untouched by the paste. + const ScDBData* pDestOwn + = rDestDoc.GetDBCollection()->getNamedDBs().findByUpperName(u"MYTABLE"_ustr); + CPPUNIT_ASSERT(pDestOwn); + ScRange aDestOwnArea; + pDestOwn->GetArea(aDestOwnArea); + CPPUNIT_ASSERT_EQUAL(ScRange(5, 10, 0, 7, 13, 0), aDestOwnArea); + CPPUNIT_ASSERT_EQUAL(100.0, rDestDoc.GetValue(5, 11, 0)); + CPPUNIT_ASSERT_EQUAL(200.0, rDestDoc.GetValue(5, 12, 0)); + CPPUNIT_ASSERT_EQUAL(400.0, rDestDoc.GetValue(5, 13, 0)); + + xDestDocSh->DoClose(); + m_pDoc->DeleteTab(0); +} + CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteFormulasExternalDoc) { SfxMedium* pMedium = new SfxMedium(u"file:///source.fake"_ustr, StreamMode::STD_READWRITE);