@Test
	public void testMergeOfOverlapOverlaysWithChanges() throws Exception {
		// FIXME This isn't actually merging. Also the overlays are considered different and don't currently overlap.
		mtf.initialize("NotepadMergeListingTest", new ProgramModifierListener() {
			@Override
			public void modifyLatest(ProgramDB program) {
				// P1 program
				try {
					Memory memory = program.getMemory();
					memory.createInitializedBlock("Foo", addr(program, "0x01000000"), 0x180L,
						(byte) 0x0, null, true);

					SymbolTable st = program.getSymbolTable();
					Namespace globalNamespace = program.getGlobalNamespace();
					st.createLabel(addr(program, "Foo:0x01000030"), "Sample0030", globalNamespace,
						SourceType.USER_DEFINED);
					st.createLabel(addr(program, "Foo:0x01000079"), "Sample0079", globalNamespace,
						SourceType.USER_DEFINED);
					st.createLabel(addr(program, "Foo:0x0100017f"), "Sample017f", globalNamespace,
						SourceType.USER_DEFINED);
				}
				catch (Exception e) {
					Assert.fail(e.getMessage());
				}
			}

			@Override
			public void modifyPrivate(ProgramDB program) {
				// P2 program
				try {
					Memory memory = program.getMemory();
					memory.createInitializedBlock("Foo", addr(program, "0x01000080"), 0x180L,
						(byte) 0x0, null, true);

					SymbolTable st = program.getSymbolTable();
					Namespace globalNamespace = program.getGlobalNamespace();
					st.createLabel(addr(program, "Foo:0x01000080"), "Other0080", globalNamespace,
						SourceType.USER_DEFINED);
					st.createLabel(addr(program, "Foo:0x01000180"), "Other0180", globalNamespace,
						SourceType.USER_DEFINED);
					// TODO: No CodeUnit at Foo:0x01000200 - outside memory block
					// Should symbols be handled by Diff/Merge outside of memory blocks?
					st.createLabel(addr(program, "Foo:0x01000200"), "Other0200", globalNamespace,
						SourceType.USER_DEFINED);
				}
				catch (Exception e) {
					Assert.fail(e.getMessage());
				}
			}
		});

		p1 = mtf.getResultProgram();
		p2 = mtf.getPrivateProgram();

		int txId = p1.startTransaction("Merge into Program 1");
		boolean commit = false;
		try {
			programMerge = new ProgramMergeManager(p1, p2);

			programMerge.setDiffFilter(new ProgramDiffFilter(ProgramDiffFilter.SYMBOL_DIFFS));

			AddressSetView diffSet = programMerge.getFilteredDifferences();

			AddressSet expectedDiffs = new AddressSet();
			expectedDiffs.addRange(addr(p1, "Foo:0x01000030"), addr(p1, "Foo:0x01000030"));
			expectedDiffs.addRange(addr(p1, "Foo:0x01000079"), addr(p1, "Foo:0x01000079"));
			expectedDiffs.addRange(addr(p1, "Foo:0x01000080"), addr(p1, "Foo:0x01000080"));
			expectedDiffs.addRange(addr(p1, "Foo:0x0100017f"), addr(p1, "Foo:0x01000180"));
			assertEquals(expectedDiffs, diffSet);

			programMerge.setMergeFilter(new ProgramMergeFilter(
				ProgramMergeFilter.SYMBOLS | ProgramMergeFilter.PRIMARY_SYMBOL,
				ProgramMergeFilter.REPLACE));

			// must intersect diff set with p1 memory to avoid skipping merge
			programMerge.merge(expectedDiffs.intersect(p1.getMemory()), TaskMonitor.DUMMY);

			diffSet = programMerge.getFilteredDifferences();
			expectedDiffs = new AddressSet();
			expectedDiffs.addRange(addr(p1, "Foo:0x01000030"), addr(p1, "Foo:0x01000030"));
			expectedDiffs.addRange(addr(p1, "Foo:0x01000079"), addr(p1, "Foo:0x01000079"));
			// Foo:0x01000180 not applied to p1 since outside defined memory block
			expectedDiffs.addRange(addr(p1, "Foo:0x01000180"), addr(p1, "Foo:0x01000180"));
			assertEquals(expectedDiffs, diffSet);

			commit = true;
		}
		finally {
			p1.endTransaction(txId, commit);
		}
	}
<CODE>ProgramMerge2Test</CODE> tests the <CODE>ProgramMerge</CODE> class
to verify it correctly merges various types of program differences.
The tests modify copies of the same program to cause differences
between these two programs, which can then be merged.
Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramMerge2Test.java