Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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