Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Scary bug (test case included): ComboBoxItem silently discarding values in > 5.0p

    Hi,

    there is a really scary problem in 5.0p and 5.1d (not in 4.1p).

    At least the following browsers are affected
    * IE 11
    * Firefox 34.0 on Windows
    * Chromium Version 39.0.2171.65 Ubuntu 14.04 (64-bit)

    I tried this with the latest nightlies (2015-01-10). A test case reproducing the problem is attached.

    The core problem is that when setting a ComboBoxItem back to empty, the ComboBoxItem SILENTLY discards that change. Instead it SILENTLY resets to the previous value!

    To make things worse, this only happens when there is
    a) another ComboBoxItem in the DynamicForm, OR
    b) another IntegerItem in the DynamicForm

    The attached test case shows this.

    Steps to reproduce:
    * DataSourceA as a source for the DynamicForm with
    - DataSourceIntegerField for ComboBoxItem-FormItem
    - DataSourceIntegerField for a IntegerItem-FormItem
    * DataSourceB as a optionDataSource with
    - DataSourceIntegerField as valueField for the ComboBoxItem
    - DataSourceTextField as displayField for the ComboBoxItem
    - Mock a few records into the DataSource to have selectable values in the ComboBoxItem
    * DynamicForm with
    - Attached to DataSourceA
    - ComboBoxItem with DataSourceB as optionDataSource and valueField and displayField set accordingly
    - Attached to ValuesManager
    * ValuesManager attached to DataSourceA and DynamicForm
    * Button
    - valuesManager.validate()
    - GWT.log(valuesManager.getChangedValues().toString());
    - valuesManager.saveData();
    * Start app
    * Select a value from the ComboBoxItem
    * Enter a value in the IntegerItem (1122)
    * Hit Save Button
    - Note that getChangedValues logs that both attributes got changed
    * Empty the ComboBoxItem (by selecting the text and hitting backspace, or any other way)
    * Hit Save Button
    - valuesManager.getChangedValues().size() is 0!!!!

    The valuesManager.validate() triggers the problem, too, but it is not necessary though: The problem also occurrs without validate() and with saveData() only. The field of the ComboBoxItem is rolled back to its original value in the record on saveData(). Using valuesManager.getChangedValues() after valuesManager.validate() is an easy way to show and test the problem. But you can do without it. Using setAddUnknownValues and setAllowEmptyValues on the ComboBoxItem does not change the false behaviour.

    The problem is really hard to discover: It DOES NOT occur, if you have only a single ComboBoxItem and no other IntegerItem ot ComboBoxItem in the DynamicForm! You can have a TextItem, a RichTextItem or a DateItem in the form, and it works as expected. Add another ComboBoxItem or an IntegerItem and the bug shows up.

    Apart from us being affected I consider this a really evil problem, since it is so hard to notice! And since it is there I assume no one has noticed it since. The thought of how many data changes have been lost since it is there without any user noticing it scares me.

    We have just finished a new version of our application and this week the User Acceptance Test starts. We were using 4.1p until shortly before christmas we discovered a bug there (we will provide a bug report for that, too, soon). This bug does not occur in 5.0p which is why we migrated to 5.0p then and noticed no problems so far, until just yesterday night we discovered this.

    It would be really helpful for us if you could fix that immediately.

    Remarks regarding the test case:
    The GwtRpcDataSource is just a dummy for clientOnlyDataSource, which is the implemented in the BugReportDataSource. The whole project serves as test case project for us, which is the reason why there is a Demo-interface.

    I added a little bit of investigation of when the bug was introduced:
    * 5.0p nightly 2014-09-14 does not show the problem
    * 5.0p nightly 2014-11-30 DOES show the problem

    Let me add that we are long time users of SmartGwt and enjoy it pretty much. I hope that you can locate the source of that bug.

    Regards,
    Andre
    Attached Files
    Last edited by SCAI_Andre; 11 Jan 2015, 03:51. Reason: formatting

    #2
    gwt.xml

    Let me add the gwt.xml, too. The forum only allows 5 attachments per post.

    There are a few legacy comments there since it is copied from another project, but it makes the test case complete.
    Attached Files

    Comment


      #3
      Forgot: The problem occurs in the LGPL version

      Comment


        #4
        Your use of a GwtRpcDataSource as well as leaving in place a bunch of unrelated code makes this a pointlessly overcomplicated test case and introduces the possibility that the flaw is actually somewhere in your DataSource implementation or other extra code.

        If you hope to see this report looked at quickly, follow the rules and make your test minimal: use an actual clientOnly DataSource and delete the extra code. The whole thing should fit neatly into one .java file and not require any setup beyond dropping it into one of the sample projects - that's what's meant by a test case.

        Comment


          #5
          Code:
          wrong code removed
          Last edited by SCAI_Andre; 12 Jan 2015, 09:11. Reason: code is wrong

          Comment


            #6
            Damn, just noticed I f**d up the copy & paste so plz ignore the code above

            Bad day

            Comment


              #7
              In the previous post I still extended the wrong datasource, even though that only needed to be changed to DataSource.

              Anyway, here is the simplified, one class, hopefully now unpointless test case.

              The gwt.xml now refers to a different EntryPoint, but I guess that is not a problem, since that file wasn't necessary anyway, as I learnt now.

              Code:
              package de.scai.client.ticket1445.defect;
              
              import com.google.gwt.core.client.EntryPoint;
              import com.google.gwt.core.client.GWT;
              import com.google.gwt.user.client.ui.RootPanel;
              import com.smartgwt.client.data.DSCallback;
              import com.smartgwt.client.data.DSRequest;
              import com.smartgwt.client.data.DSResponse;
              import com.smartgwt.client.data.DataSource;
              import com.smartgwt.client.data.fields.DataSourceIntegerField;
              import com.smartgwt.client.data.fields.DataSourceTextField;
              import com.smartgwt.client.util.SC;
              import com.smartgwt.client.widgets.Button;
              import com.smartgwt.client.widgets.events.ClickEvent;
              import com.smartgwt.client.widgets.events.ClickHandler;
              import com.smartgwt.client.widgets.form.DynamicForm;
              import com.smartgwt.client.widgets.form.ValuesManager;
              import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
              import com.smartgwt.client.widgets.form.fields.IntegerItem;
              import com.smartgwt.client.widgets.grid.ListGridRecord;
              import com.smartgwt.client.widgets.layout.Layout;
              import com.smartgwt.client.widgets.layout.VLayout;
              
              public class DemoTicket1445 implements EntryPoint {
              
                  private Layout layoutRoot;
                  /**
                   * This is the entry point method.
                   */
                  public void onModuleLoad() {
                      layoutRoot = new VLayout();
              
                      RootPanel.get("gwtContainer").add(layoutRoot);
              
                      run();
                  }
              
                  private class UserDataSource extends DataSource {
                      public static final String FIELDNAME_ID = "FIELD_id";
                      public static final String FIELDNAME_GROUPID = "FIELD_groupId";
                      public static final String FIELDNAME_WAGE = "FIELD_wage";
              
                      protected DataSourceIntegerField id = new DataSourceIntegerField(FIELDNAME_ID, "Id");
                      protected DataSourceIntegerField groupId = new DataSourceIntegerField(FIELDNAME_GROUPID, "Group");
                      protected DataSourceIntegerField wage = new DataSourceIntegerField(FIELDNAME_WAGE, "Wage");
              
                      private UserDataSource() {
                          super();
                          id.setPrimaryKey(true);
                          addField(id);
                          addField(groupId);
                          addField(wage);
                          setClientOnly(true);
                          setCacheAllData(true);
                      }
              
                  }
              
                  private class GroupDataSource extends DataSource {
                      public static final String FIELDNAME_ID = "FIELD_id";
                      public static final String FIELDNAME_GROUPNAME = "FIELD_groupName";
              
                      protected DataSourceIntegerField id = new DataSourceIntegerField(FIELDNAME_ID, "Id");
                      protected DataSourceTextField groupName = new DataSourceTextField(FIELDNAME_GROUPNAME, "Groupname");
              
                      private GroupDataSource() {
                          super();
                          setFields(id, groupName);
              
                          setClientOnly(true);
                          setCacheAllData(true);
                          initData();
                      }
              
                      public void initData() {
                          final int recordCount = 5;
                          final ListGridRecord[] data = new ListGridRecord[recordCount];
                          for (int i = 0; i <= recordCount; i++) {
                              ListGridRecord listGridRecord = new ListGridRecord();
                              listGridRecord.setAttribute(FIELDNAME_ID, i);
                              listGridRecord.setAttribute(FIELDNAME_GROUPNAME, "dummyGroup"+ Integer.toString(i));
                              data[i] = listGridRecord;
                          }
                          setCacheData(data);
                      }
                  }
              
                  private class UserDynamicForm extends DynamicForm {
                      protected ComboBoxItem groupName = new ComboBoxItem(UserDataSource.FIELDNAME_GROUPID, "Groupname");
                      protected IntegerItem wage = new IntegerItem(UserDataSource.FIELDNAME_WAGE, "Wage");
              
                      protected void setComboBox(ComboBoxItem comboBoxItem, DataSource dataSource, String valueField, String displayField) {
                          dataSource.setShowPrompt(false);
                          comboBoxItem.setOptionDataSource(dataSource);
              
                          comboBoxItem.setValueField(valueField);
                          comboBoxItem.setDisplayField(displayField);
                      }
              
                      public void initForm(GroupDataSource groupDataSource) {
                          setComboBox(groupName, groupDataSource, GroupDataSource.FIELDNAME_ID, GroupDataSource.FIELDNAME_GROUPNAME);
              
                          this.setFields(
                                  groupName
                                  , wage
                          );
                      }
                  }
              
                  private GroupDataSource groupDataSource;
                  private UserDataSource userDataSource;
                  private ValuesManager valuesManager;
                  private UserDynamicForm userDynamicForm;
              
                  public void run() {
              
                      userDataSource = new UserDataSource();
                      groupDataSource = new GroupDataSource();
              
                      valuesManager = new ValuesManager();
                      valuesManager.setDataSource(userDataSource);
              
                      initForm();
              
                      Layout layout = new VLayout();
                      layout.addMember(userDynamicForm);
                      layoutRoot.addMember(layout);
              
                      Button save = new Button("Save");
                      save.addClickHandler(new ClickHandler() {
                          @Override
                          public void onClick(ClickEvent clickEvent) {
                              if (valuesManager.validate()) {
                                  GWT.log(valuesManager.getChangedValues().toString());
                                  if (valuesManager.getChangedValues().size() == 0) {
                                      SC.warn("No changed values");
                                  }
                                  valuesManager.saveData(new DSCallback() {
                                      @Override
                                      public void execute(DSResponse dsResponse, Object o, DSRequest dsRequest) {
                                          GWT.log("dsResponse.result is: " + Integer.toString(dsResponse.getStatus()));
                                      }
                                  });
                              }
                          }
                      });
                      layout.addMember(save);
              
                      valuesManager.addMember(userDynamicForm);
                      valuesManager.editNewRecord();
                  }
              
                  private void initForm() {
                      userDynamicForm = new UserDynamicForm();
                      userDynamicForm.initForm(groupDataSource);
                      userDynamicForm.setDataSource(userDataSource);
                  }
              
              }

              Comment


                #8
                Thanks for the simplified test case, that helps a lot in terms of pinning down the bug.

                This is fixed for affected builds - grab the next nightly for the fix.

                Comment


                  #9
                  tested nightly 5.0p/LGPL/2015-01-14:
                  can confirm it is fixed

                  Thank you!

                  Comment

                  Working...
                  X