Skip to content

Different result with batched parameter when one list is empty #7

@pawelsa

Description

@pawelsa

When performing diff between two lists where the compared list is empty results with different results depending on the batched parameter. To simplify the problem I've created a sample code so that it can be easily tested.

import 'package:diffutil_dart/diffutil.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {

  test("empty -> [...6 items] should have 6 inserts operations with consecutive positions and count 1 when not batched", () {
    final apiList = [
      SimpleClass("1", "Content 1"),
      SimpleClass("2", "Content 2"),
      SimpleClass("3", "Content 3"),
      SimpleClass("4", "Content 4"),
      SimpleClass("5", "Content 5"),
      SimpleClass("6", "Content 6"),
    ];
    final dbList = <SimpleClass>[];

    final diff = calculateDiff(
      SimpleClassDiffDelegate(dbList, apiList),
      detectMoves: true,
    ).getUpdates(batch: false).toList();
    expect(diff, const [
      Insert(position: 0, count: 1),
      Insert(position: 1, count: 1),
      Insert(position: 2, count: 1),
      Insert(position: 3, count: 1),
      Insert(position: 4, count: 1),
      Insert(position: 5, count: 1),
    ]);
  });

  test("empty -> [...6 items] should have 1 insert operation with count 6 on position 0 when batched", () {
    final apiList = [
      SimpleClass("1", "Content 1"),
      SimpleClass("2", "Content 2"),
      SimpleClass("3", "Content 3"),
      SimpleClass("4", "Content 4"),
      SimpleClass("5", "Content 5"),
      SimpleClass("6", "Content 6"),
    ];
    final dbList = <SimpleClass>[];

    final diff = calculateDiff(
      SimpleClassDiffDelegate(dbList, apiList),
      detectMoves: true,
    ).getUpdates(batch: true).toList();
    expect(diff, const [
      Insert(position: 0, count: 6),
    ]);
  });

  test("[...6 items] -> empty list should have 6 remove operations with 6 consecutive positions and count 1 when not batched", () {
    final apiList = <SimpleClass>[];
    final dbList = [
      SimpleClass("1", "Content 1"),
      SimpleClass("2", "Content 2"),
      SimpleClass("3", "Content 3"),
      SimpleClass("4", "Content 4"),
      SimpleClass("5", "Content 5"),
      SimpleClass("6", "Content 6"),
    ];

    final diff = calculateDiff(
      SimpleClassDiffDelegate(dbList, apiList),
      detectMoves: true,
    ).getUpdates(batch: false).toList();
    expect(diff, const [
      Remove(position: 5, count: 1),
      Remove(position: 4, count: 1),
      Remove(position: 3, count: 1),
      Remove(position: 2, count: 1),
      Remove(position: 1, count: 1),
      Remove(position: 0, count: 1),
    ]);
  });

  test("[...6 items] -> empty list should have 1 remove operation with position 0 and count 6 when batched", () {
    final apiList = <SimpleClass>[];
    final dbList = [
      SimpleClass("1", "Content 1"),
      SimpleClass("2", "Content 2"),
      SimpleClass("3", "Content 3"),
      SimpleClass("4", "Content 4"),
      SimpleClass("5", "Content 5"),
      SimpleClass("6", "Content 6"),
    ];

    final diff = calculateDiff(
      SimpleClassDiffDelegate(dbList, apiList),
      detectMoves: true,
    ).getUpdates(batch: true).toList();
    expect(diff, const [
      Remove(position: 0, count: 6),
    ]);
  });
}

class SimpleClass {
  final String id;
  final String content;

  SimpleClass(this.id, this.content);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is SimpleClass &&
          runtimeType == other.runtimeType &&
          id == other.id &&
          content == other.content;

  @override
  int get hashCode => id.hashCode ^ content.hashCode;
}

class SimpleClassDiffDelegate implements DiffDelegate {
  final List<SimpleClass> _oldList;
  final List<SimpleClass> _newList;

  SimpleClassDiffDelegate(this._oldList, this._newList);

  @override
  bool areContentsTheSame(int oldItemPosition, int newItemPosition) => _oldList[oldItemPosition] == _newList[newItemPosition];

  @override
  bool areItemsTheSame(int oldItemPosition, int newItemPosition) => _oldList[oldItemPosition].id == _newList[newItemPosition].id;

  @override
  Object getChangePayload(int oldItemPosition, int newItemPosition) => _newList[newItemPosition];

  @override
  int getNewListSize() => _newList.length;

  @override
  int getOldListSize() => _oldList.length;
}

In the first test the result is:

Expected: [
            Insert:Insert{position: 0, count: 1},
            Insert:Insert{position: 1, count: 1},
            Insert:Insert{position: 2, count: 1},
            Insert:Insert{position: 3, count: 1},
            Insert:Insert{position: 4, count: 1},
            Insert:Insert{position: 5, count: 1}
          ]
  Actual: [
            Insert:Insert{position: 0, count: 1},
            Insert:Insert{position: 0, count: 1},
            Insert:Insert{position: 0, count: 1},
            Insert:Insert{position: 0, count: 1},
            Insert:Insert{position: 0, count: 1},
            Insert:Insert{position: 0, count: 1}
          ]
   Which: at location [1] is Insert:<Insert{position: 0, count: 1}> instead of Insert:<Insert{position: 1, count: 1}>

I think that we should obtain the result as in the test case, so that we can easily create a list of all items added.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions